commit a720750e46e31f535ab6c09329e1c06abbed07ef Author: AnnulusGames Date: Sat Dec 2 10:54:41 2023 +0900 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65723c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# DS_Store +*.DS_Store diff --git a/Alchemy.SourceGenerator/.gitignore b/Alchemy.SourceGenerator/.gitignore new file mode 100644 index 0000000..8f060be --- /dev/null +++ b/Alchemy.SourceGenerator/.gitignore @@ -0,0 +1,403 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +.idea + +## DS_Store +*.DS_Store \ No newline at end of file diff --git a/Alchemy.SourceGenerator/Alchemy.SourceGenerator.csproj b/Alchemy.SourceGenerator/Alchemy.SourceGenerator.csproj new file mode 100644 index 0000000..05b2597 --- /dev/null +++ b/Alchemy.SourceGenerator/Alchemy.SourceGenerator.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + 9.0 + + + + + + + + + diff --git a/Alchemy.SourceGenerator/Alchemy.SourceGenerator.sln b/Alchemy.SourceGenerator/Alchemy.SourceGenerator.sln new file mode 100644 index 0000000..5e0d4e4 --- /dev/null +++ b/Alchemy.SourceGenerator/Alchemy.SourceGenerator.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 25.0.1706.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alchemy.SourceGenerator", "Alchemy.SourceGenerator.csproj", "{073160C2-08C9-4905-A4D0-7897AADB4E6C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {073160C2-08C9-4905-A4D0-7897AADB4E6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {073160C2-08C9-4905-A4D0-7897AADB4E6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {073160C2-08C9-4905-A4D0-7897AADB4E6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {073160C2-08C9-4905-A4D0-7897AADB4E6C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D02A2C17-448E-4F43-BF08-7E77D0E2208B} + EndGlobalSection +EndGlobal diff --git a/Alchemy.SourceGenerator/AlchemySerializeGenerator.cs b/Alchemy.SourceGenerator/AlchemySerializeGenerator.cs new file mode 100644 index 0000000..e73dbeb --- /dev/null +++ b/Alchemy.SourceGenerator/AlchemySerializeGenerator.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Alchemy.SourceGenerator +{ + [Generator] + public sealed class AlchemySerializeGenerator : ISourceGenerator + { + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(SyntaxReceiver.Create); + } + + public void Execute(GeneratorExecutionContext context) + { + if (context.SyntaxReceiver is not SyntaxReceiver receiver || receiver.TargetTypes.Count == 0) return; + + var compilation = context.Compilation; + + try + { + foreach (var typeSyntax in receiver.TargetTypes) + { + var fieldSymbols = new List(); + var fields = typeSyntax.Members + .Where(x => x is FieldDeclarationSyntax) + .Select(x => (FieldDeclarationSyntax)x); + foreach (var field in fields) + { + var model = context.Compilation.GetSemanticModel(field.SyntaxTree); + foreach (var variable in field.Declaration.Variables) + { + var fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol; + var attribute = fieldSymbol.GetAttributes() + .FirstOrDefault(x => + x.AttributeClass.Name is "AlchemySerializeField" + or "AlchemySerializeFieldAttribute" + or "Alchemy.Serialization.AlchemySerializeField" + or "Alchemy.Serialization.AlchemySerializeFieldAttribute"); + if (attribute != null) + { + fieldSymbols.Add(fieldSymbol); + } + } + } + + var typeSymbol = context.Compilation.GetSemanticModel(typeSyntax.SyntaxTree) + .GetDeclaredSymbol(typeSyntax); + + var sourceText = ProcessClass((INamedTypeSymbol)typeSymbol, fieldSymbols); + var fullType = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + .Replace("global::", "") + .Replace("<", "_") + .Replace(">", "_"); + + context.AddSource(fullType + ".AlchemySerializeGenerator.g.cs", sourceText); + } + } + catch (Exception ex) + { + var diagnosticDescriptor = new DiagnosticDescriptor("AlchemySerializeGeneratorError", "AlchemySerializeGeneratorError", $"Generation failed with:\n {ex}", "AlchemySerializeGeneratorError", DiagnosticSeverity.Error, true); + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, Location.None, DiagnosticSeverity.Error)); + } + } + + static string ProcessClass(INamedTypeSymbol typeSymbol, List fieldSymbols) + { + var onAfterDeserializeCodeBuilder = new StringBuilder(); + var onBeforeSerializeCodeBuilder = new StringBuilder(); + var serializationDataCodeBuilder = new StringBuilder(); + + var hasShowSerializationData = typeSymbol.GetAttributes().Any(x => x.AttributeClass.Name + is "ShowAlchemySerializationData" + or "ShowAlchemySerializationDataAttribute" + or "Alchemy.Serialization.ShowAlchemySerializationData" + or "Alchemy.Serialization.ShowAlchemySerializationDataAttribute"); + + var serializationDataAttibutesCode = hasShowSerializationData ? "[global::Alchemy.Inspector.ReadOnly, global::UnityEngine.TextArea(3, 999), global::UnityEngine.SerializeField]" : "[global::UnityEngine.HideInInspector, global::UnityEngine.SerializeField]"; + + // target class namespace + var ns = typeSymbol.ContainingNamespace.IsGlobalNamespace ? string.Empty : $"namespace {typeSymbol.ContainingNamespace} {{"; + + foreach (var field in fieldSymbols) + { + var serializeCode = +@$"try +{{ + alchemySerializationData.{field.Name}.data = global::Alchemy.Serialization.Internal.SerializationHelper.ToJson(this.{field.Name} , alchemySerializationData.UnityObjectReferences); + alchemySerializationData.{field.Name}.isCreated = true; +}} +catch (global::System.Exception ex) +{{ + global::UnityEngine.Debug.LogException(ex); +}}"; + + var deserializeCode = +@$"try +{{ + if (alchemySerializationData.{field.Name}.isCreated) + {{ + this.{field.Name} = global::Alchemy.Serialization.Internal.SerializationHelper.FromJson<{field.Type.ToDisplayString()}>(alchemySerializationData.{field.Name}.data, alchemySerializationData.UnityObjectReferences); + }} +}} +catch (global::System.Exception ex) +{{ + global::UnityEngine.Debug.LogException(ex); +}}"; + + onBeforeSerializeCodeBuilder.AppendLine(serializeCode); + onAfterDeserializeCodeBuilder.AppendLine(deserializeCode); + + serializationDataCodeBuilder.Append("public Item ").Append(field.Name).Append(" = new();"); + } + + return +@$" +// +{ns} + + partial class {typeSymbol.Name} : global::UnityEngine.ISerializationCallbackReceiver + {{ + void global::UnityEngine.ISerializationCallbackReceiver.OnAfterDeserialize() + {{ + {onAfterDeserializeCodeBuilder} + if (this is global::Alchemy.Serialization.IAlchemySerializationCallbackReceiver receiver) receiver.OnAfterDeserialize(); + }} + + void global::UnityEngine.ISerializationCallbackReceiver.OnBeforeSerialize() + {{ + if (this is global::Alchemy.Serialization.IAlchemySerializationCallbackReceiver receiver) receiver.OnBeforeSerialize(); + alchemySerializationData.UnityObjectReferences.Clear(); + {onBeforeSerializeCodeBuilder} + }} + + [global::System.Serializable] + sealed class AlchemySerializationData + {{ + {serializationDataCodeBuilder} + + [global::UnityEngine.SerializeField] private global::System.Collections.Generic.List unityObjectReferences = new(); + public global::System.Collections.Generic.IList UnityObjectReferences => unityObjectReferences; + + [global::System.Serializable] + public sealed class Item + {{ + [global::UnityEngine.HideInInspector] public bool isCreated = false; + [global::UnityEngine.TextArea(1, 999)] public string data; + }} + }} + + {serializationDataAttibutesCode} private AlchemySerializationData alchemySerializationData = new(); + }} + +{(string.IsNullOrEmpty(ns) ? "" : "}")} +"; + } + + sealed class SyntaxReceiver : ISyntaxReceiver + { + internal static ISyntaxReceiver Create() + { + return new SyntaxReceiver(); + } + + public List TargetTypes { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax) + { + var hasAttribute = typeDeclarationSyntax.AttributeLists + .SelectMany(x => x.Attributes) + .Any(x => x.Name.ToString() + is "AlchemySerialize" + or "AlchemySerializeAttribute" + or "Alchemy.Serialization.AlchemySerialize" + or "Alchemy.Serialization.AlchemySerializeAttribute"); + if (hasAttribute) + { + TargetTypes.Add(typeDeclarationSyntax); + } + } + } + } + } +} \ No newline at end of file diff --git a/Alchemy.SourceGenerator/DiagnosticDescriptors.cs b/Alchemy.SourceGenerator/DiagnosticDescriptors.cs new file mode 100644 index 0000000..dd3330c --- /dev/null +++ b/Alchemy.SourceGenerator/DiagnosticDescriptors.cs @@ -0,0 +1,4 @@ +namespace Alchemy.SourceGenerator +{ + +} \ No newline at end of file diff --git a/Alchemy/.editorconfig b/Alchemy/.editorconfig new file mode 100644 index 0000000..063a688 --- /dev/null +++ b/Alchemy/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# IDE0027: アクセサーに式本体を使用する +csharp_style_expression_bodied_accessors = false diff --git a/Alchemy/.gitignore b/Alchemy/.gitignore new file mode 100644 index 0000000..2987666 --- /dev/null +++ b/Alchemy/.gitignore @@ -0,0 +1,78 @@ +# This .gitignore file should be placed at the root of your Unity project directory +# +# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore +# +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ + +# MemoryCaptures can get excessive in size. +# They also could contain extremely sensitive data +/[Mm]emoryCaptures/ + +# Recordings can get excessive in size +/[Rr]ecordings/ + +# Uncomment this line if you wish to ignore the asset store tools plugin +# /[Aa]ssets/AssetStoreTools* + +# Autogenerated Jetbrains Rider plugin +/[Aa]ssets/Plugins/Editor/JetBrains* + +# Visual Studio cache directory +.vs/ + +# Gradle cache directory +.gradle/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity3D generated file on crash reports +sysinfo.txt + +# Builds +*.apk +*.aab +*.unitypackage +*.app + +# Crashlytics generated file +crashlytics-build.properties + +# Packed Addressables +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* + +# Temporary auto-generated Android Assets +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* + +# Visual Studio Code +.vscode + +## DS_Store +*.DS_Store diff --git a/Alchemy/Assets/Alchemy.meta b/Alchemy/Assets/Alchemy.meta new file mode 100644 index 0000000..8770a62 --- /dev/null +++ b/Alchemy/Assets/Alchemy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4def02a2f2f60434e82ef3fd321ee6be +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Alchemy/Assets/Alchemy/Editor.meta b/Alchemy/Assets/Alchemy/Editor.meta new file mode 100644 index 0000000..ec28ecd --- /dev/null +++ b/Alchemy/Assets/Alchemy/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 285cbd8c7df70446c901672504e736c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Alchemy/Assets/Alchemy/Editor/Alchemy.Editor.asmdef b/Alchemy/Assets/Alchemy/Editor/Alchemy.Editor.asmdef new file mode 100644 index 0000000..94d5a09 --- /dev/null +++ b/Alchemy/Assets/Alchemy/Editor/Alchemy.Editor.asmdef @@ -0,0 +1,25 @@ +{ + "name": "Alchemy.Editor", + "rootNamespace": "Alchemy.Editor", + "references": [ + "GUID:88be65f96b86746888c927a5c8ff3534", + "GUID:2765e68924a08a94ea0ea66b31c0168f" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.serialization", + "expression": "", + "define": "ALCHEMY_SUPPORT_SERIALIZATION" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Alchemy/Assets/Alchemy/Editor/Alchemy.Editor.asmdef.meta b/Alchemy/Assets/Alchemy/Editor/Alchemy.Editor.asmdef.meta new file mode 100644 index 0000000..ab70161 --- /dev/null +++ b/Alchemy/Assets/Alchemy/Editor/Alchemy.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5fd453cd0d182422093c4a764fd5eadb +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Alchemy/Assets/Alchemy/Editor/AlchemyEditor.cs b/Alchemy/Assets/Alchemy/Editor/AlchemyEditor.cs new file mode 100644 index 0000000..14e7dfe --- /dev/null +++ b/Alchemy/Assets/Alchemy/Editor/AlchemyEditor.cs @@ -0,0 +1,71 @@ +using System.Reflection; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using Alchemy.Inspector; +using Alchemy.Editor.Internal; +using UnityEditor.UIElements; +#if ALCHEMY_SUPPORT_SERIALIZATION +using Alchemy.Serialization; +#endif + +namespace Alchemy.Editor +{ + using Editor = UnityEditor.Editor; + + /// + /// Editor base class for Inspector drawing in Alchemy + /// + public abstract class AlchemyEditor : Editor + { + const string ScriptFieldName = "m_Script"; +#if ALCHEMY_SUPPORT_SERIALIZATION + const string AlchemySerializationWarning = "In the current version, fields with the [AlchemySerializedField] attribute do not support editing multiple objects."; +#endif + + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + var targetType = target.GetType(); + + if (targetType.HasCustomAttribute()) + { + // Create default inspector + InspectorElement.FillDefaultInspector(root, serializedObject, this); + return root; + } + +#if ALCHEMY_SUPPORT_SERIALIZATION + if (targetType.HasCustomAttribute() && targets.Length > 1) + { + root.Add(new HelpBox(AlchemySerializationWarning, HelpBoxMessageType.Error)); + } +#endif + + // Add script field + if (targetType.GetCustomAttribute() == null) + { + var scriptField = new PropertyField(serializedObject.FindProperty(ScriptFieldName)); + scriptField.SetEnabled(false); + root.Add(scriptField); + root.Add(new VisualElement() + { + style = { height = EditorGUIUtility.standardVerticalSpacing * 0.5f } + }); + } + + // Add elements + InspectorHelper.BuildElements(serializedObject, root, target, name => serializedObject.FindProperty(name), 0); + + return root; + } + } + + [CustomEditor(typeof(MonoBehaviour), editorForChildClasses: true, isFallback = true)] + [CanEditMultipleObjects] + internal sealed class MonoBehaviourEditor : AlchemyEditor { } + + [CustomEditor(typeof(ScriptableObject), editorForChildClasses: true, isFallback = true)] + [CanEditMultipleObjects] + internal sealed class ScriptableObjectEditor : AlchemyEditor { } +} \ No newline at end of file diff --git a/Alchemy/Assets/Alchemy/Editor/AlchemyEditor.cs.meta b/Alchemy/Assets/Alchemy/Editor/AlchemyEditor.cs.meta new file mode 100644 index 0000000..78da399 --- /dev/null +++ b/Alchemy/Assets/Alchemy/Editor/AlchemyEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09b0ba97e97f24db4a0dce5a1e942810 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Alchemy/Assets/Alchemy/Editor/BuiltinPropertyGroupDrawers.cs b/Alchemy/Assets/Alchemy/Editor/BuiltinPropertyGroupDrawers.cs new file mode 100644 index 0000000..22c483d --- /dev/null +++ b/Alchemy/Assets/Alchemy/Editor/BuiltinPropertyGroupDrawers.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor; +using UnityEditor.UIElements; +using Alchemy.Inspector; +using Alchemy.Editor.Internal; +using Alchemy.Editor.Elements; + +namespace Alchemy.Editor.GroupDrawers +{ + [CustomPropertyGroupDrawer(typeof(GroupAttribute))] + public sealed class GroupDrawer : PropertyGroupDrawer + { + public override VisualElement CreateRootElement(string label) + { + return new Box() + { + style = { + width = Length.Percent(100f), + marginTop = 3f, + paddingBottom = 2f, + paddingRight = 1f, + paddingLeft = 1f, + } + }; + } + } + + [CustomPropertyGroupDrawer(typeof(BoxGroupAttribute))] + public sealed class BoxGroupDrawer : PropertyGroupDrawer + { + public override VisualElement CreateRootElement(string label) + { + var helpBox = new HelpBox() + { + text = label, + style = { + flexDirection = FlexDirection.Column, + width = Length.Percent(100f), + marginTop = 3f, + paddingBottom = 3f, + paddingRight = 3f, + paddingLeft = 3f, + } + }; + + var labelElement = helpBox.Q