mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Xaml UI (#1446)
Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,6 +4,9 @@
|
|||||||
[submodule "Lidgren.Network"]
|
[submodule "Lidgren.Network"]
|
||||||
path = Lidgren.Network/Lidgren.Network
|
path = Lidgren.Network/Lidgren.Network
|
||||||
url = https://github.com/space-wizards/lidgren-network-gen3.git
|
url = https://github.com/space-wizards/lidgren-network-gen3.git
|
||||||
|
[submodule "XamlX"]
|
||||||
|
path = XamlX
|
||||||
|
url = https://github.com/space-wizards/XamlX
|
||||||
[submodule "Robust.LoaderApi"]
|
[submodule "Robust.LoaderApi"]
|
||||||
path = Robust.LoaderApi
|
path = Robust.LoaderApi
|
||||||
url = https://github.com/space-wizards/Robust.LoaderApi.git
|
url = https://github.com/space-wizards/Robust.LoaderApi.git
|
||||||
|
|||||||
24
MSBuild/XamlIL.targets
Normal file
24
MSBuild/XamlIL.targets
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="**\*.xaml.cs">
|
||||||
|
<DependentUpon>%(Filename)</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="**\*.xaml" />
|
||||||
|
<AdditionalFiles Include="**\*.xaml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
|
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<UsingTask TaskName="CompileRobustXamlTask" AssemblyFile="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(Configuration)\net5.0\Robust.Client.Injectors.dll" />
|
||||||
|
<Target Name="CompileRobustXaml" AfterTargets="AfterCompile">
|
||||||
|
<PropertyGroup>
|
||||||
|
<RobustXamlReferencesTemporaryFilePath Condition="'$(RobustXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)XAML/references</RobustXamlReferencesTemporaryFilePath>
|
||||||
|
<RobustXamlOriginalCopyFilePath Condition="'$(RobustXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)XAML/original.dll</RobustXamlOriginalCopyFilePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<WriteLinesToFile File="$(RobustXamlReferencesTemporaryFilePath)" Lines="@(ReferencePathWithRefAssemblies)" Overwrite="true" />
|
||||||
|
<CompileRobustXamlTask AssemblyFile="@(IntermediateAssembly)" ReferencesFilePath="$(RobustXamlReferencesTemporaryFilePath)" OriginalCopyPath="$(RobustXamlOriginalCopyFilePath)" ProjectDirectory="$(MSBuildProjectDirectory)" AssemblyOriginatorKeyFile="$(AssemblyOriginatorKeyFile)" SignAssembly="$(SignAssembly)" DelaySign="$(DelaySign)" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
86
Robust.Client.Injectors/CompileRobustXamlTask.cs
Normal file
86
Robust.Client.Injectors/CompileRobustXamlTask.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Build.Framework;
|
||||||
|
|
||||||
|
namespace Robust.Build.Tasks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
|
||||||
|
/// </summary>
|
||||||
|
public class CompileRobustXamlTask : ITask
|
||||||
|
{
|
||||||
|
public bool Execute()
|
||||||
|
{
|
||||||
|
//Debugger.Launch();
|
||||||
|
OutputPath = OutputPath ?? AssemblyFile;
|
||||||
|
var outputPdb = GetPdbPath(OutputPath);
|
||||||
|
var input = AssemblyFile;
|
||||||
|
var inputPdb = GetPdbPath(input);
|
||||||
|
// Make a copy and delete the original file to prevent MSBuild from thinking that everything is OK
|
||||||
|
if (OriginalCopyPath != null)
|
||||||
|
{
|
||||||
|
File.Copy(AssemblyFile, OriginalCopyPath, true);
|
||||||
|
input = OriginalCopyPath;
|
||||||
|
File.Delete(AssemblyFile);
|
||||||
|
|
||||||
|
if (File.Exists(inputPdb))
|
||||||
|
{
|
||||||
|
var copyPdb = GetPdbPath(OriginalCopyPath);
|
||||||
|
File.Copy(inputPdb, copyPdb, true);
|
||||||
|
File.Delete(inputPdb);
|
||||||
|
inputPdb = copyPdb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = $"CompileRobustXamlTask -> AssemblyFile:{AssemblyFile}, ProjectDirectory:{ProjectDirectory}, OutputPath:{OutputPath}";
|
||||||
|
BuildEngine.LogMessage(msg, MessageImportance.High);
|
||||||
|
|
||||||
|
var res = XamlCompiler.Compile(BuildEngine, input,
|
||||||
|
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
|
||||||
|
ProjectDirectory, OutputPath,
|
||||||
|
(SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null);
|
||||||
|
if (!res.success)
|
||||||
|
return false;
|
||||||
|
if (!res.writtentofile)
|
||||||
|
{
|
||||||
|
File.Copy(input, OutputPath, true);
|
||||||
|
if(File.Exists(inputPdb))
|
||||||
|
File.Copy(inputPdb, outputPdb, true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string ReferencesFilePath { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string ProjectDirectory { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string AssemblyFile { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string OriginalCopyPath { get; set; }
|
||||||
|
|
||||||
|
public string OutputPath { get; set; }
|
||||||
|
|
||||||
|
public string AssemblyOriginatorKeyFile { get; set; }
|
||||||
|
public bool SignAssembly { get; set; }
|
||||||
|
public bool DelaySign { get; set; }
|
||||||
|
|
||||||
|
// shamelessly copied from avalonia
|
||||||
|
string GetPdbPath(string p)
|
||||||
|
{
|
||||||
|
var d = Path.GetDirectoryName(p);
|
||||||
|
var f = Path.GetFileNameWithoutExtension(p);
|
||||||
|
var rv = f + ".pdb";
|
||||||
|
if (d != null)
|
||||||
|
rv = Path.Combine(d, rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBuildEngine BuildEngine { get; set; }
|
||||||
|
public ITaskHost HostObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Robust.Client.Injectors/Extensions.cs
Normal file
16
Robust.Client.Injectors/Extensions.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Microsoft.Build.Framework;
|
||||||
|
|
||||||
|
namespace Robust.Build.Tasks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Taken from https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/Extensions.cs
|
||||||
|
/// </summary>
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
//shamefully copied from avalonia
|
||||||
|
public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp)
|
||||||
|
{
|
||||||
|
engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Robust.Client.Injectors/Program.cs
Normal file
62
Robust.Client.Injectors/Program.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.Build.Framework;
|
||||||
|
|
||||||
|
namespace Robust.Build.Tasks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/Program.cs
|
||||||
|
/// </summary>
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length != 3)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("expected: input references output");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CompileRobustXamlTask
|
||||||
|
{
|
||||||
|
AssemblyFile = args[0],
|
||||||
|
ReferencesFilePath = args[1],
|
||||||
|
OutputPath = args[2],
|
||||||
|
BuildEngine = new ConsoleBuildEngine(),
|
||||||
|
ProjectDirectory = Directory.GetCurrentDirectory()
|
||||||
|
}.Execute() ? 0 : 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConsoleBuildEngine : IBuildEngine
|
||||||
|
{
|
||||||
|
public void LogErrorEvent(BuildErrorEventArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"ERROR: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogWarningEvent(BuildWarningEventArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"WARNING: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogMessageEvent(BuildMessageEventArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"MESSAGE: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogCustomEvent(CustomBuildEventArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"CUSTOM: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties,
|
||||||
|
IDictionary targetOutputs) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public bool ContinueOnError { get; }
|
||||||
|
public int LineNumberOfTaskNode { get; }
|
||||||
|
public int ColumnNumberOfTaskNode { get; }
|
||||||
|
public string ProjectFileOfTaskNode { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Robust.Client.Injectors/Robust.Client.Injectors.csproj
Normal file
16
Robust.Client.Injectors/Robust.Client.Injectors.csproj
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Build.Framework" Version="16.8.0" />
|
||||||
|
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\XamlX\src\XamlX.IL.Cecil\XamlX.IL.Cecil.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
201
Robust.Client.Injectors/RobustXamlILCompiler.cs
Normal file
201
Robust.Client.Injectors/RobustXamlILCompiler.cs
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using XamlX.Ast;
|
||||||
|
using XamlX.Emit;
|
||||||
|
using XamlX.IL;
|
||||||
|
using XamlX.Transform;
|
||||||
|
using XamlX.TypeSystem;
|
||||||
|
|
||||||
|
namespace Robust.Build.Tasks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Emitters & Transformers based on:
|
||||||
|
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlRootObjectScopeTransformer.cs
|
||||||
|
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
|
||||||
|
/// </summary>
|
||||||
|
public class RobustXamlILCompiler : XamlILCompiler
|
||||||
|
{
|
||||||
|
public RobustXamlILCompiler(TransformerConfiguration configuration, XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> emitMappings, bool fillWithDefaults) : base(configuration, emitMappings, fillWithDefaults)
|
||||||
|
{
|
||||||
|
Transformers.Add(new AddNameScopeRegistration());
|
||||||
|
Transformers.Add(new RobustMarkRootObjectScopeNode());
|
||||||
|
|
||||||
|
Emitters.Add(new AddNameScopeRegistration.Emitter());
|
||||||
|
Emitters.Add(new RobustMarkRootObjectScopeNode.Emitter());
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddNameScopeRegistration : IXamlAstTransformer
|
||||||
|
{
|
||||||
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
|
||||||
|
{
|
||||||
|
if (node is XamlPropertyAssignmentNode pa)
|
||||||
|
{
|
||||||
|
if (pa.Property.Name == "Name"
|
||||||
|
&& pa.Property.DeclaringType.FullName == "Robust.Client.UserInterface.Control")
|
||||||
|
{
|
||||||
|
if (context.ParentNodes().FirstOrDefault() is XamlManipulationGroupNode mg
|
||||||
|
&& mg.Children.OfType<RobustNameScopeRegistrationXamlIlNode>().Any())
|
||||||
|
return node;
|
||||||
|
|
||||||
|
IXamlAstValueNode value = null;
|
||||||
|
for (var c = 0; c < pa.Values.Count; c++)
|
||||||
|
if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String))
|
||||||
|
{
|
||||||
|
value = pa.Values[c];
|
||||||
|
if (!(value is XamlAstTextNode))
|
||||||
|
{
|
||||||
|
var local = new XamlAstCompilerLocalNode(value);
|
||||||
|
// Wrap original in local initialization
|
||||||
|
pa.Values[c] = new XamlAstLocalInitializationNodeEmitter(value, value, local);
|
||||||
|
// Use local
|
||||||
|
value = local;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
var objectType = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().FirstOrDefault()?.Type.GetClrType();
|
||||||
|
return new XamlManipulationGroupNode(pa)
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
pa,
|
||||||
|
new RobustNameScopeRegistrationXamlIlNode(value, objectType)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*else if (pa.Property.CustomAttributes.Select(attr => attr.Type).Intersect(context.Configuration.TypeMappings.DeferredContentPropertyAttributes).Any())
|
||||||
|
{
|
||||||
|
pa.Values[pa.Values.Count - 1] =
|
||||||
|
new NestedScopeMetadataNode(pa.Values[pa.Values.Count - 1]);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RobustNameScopeRegistrationXamlIlNode : XamlAstNode, IXamlAstManipulationNode
|
||||||
|
{
|
||||||
|
public IXamlAstValueNode Name { get; set; }
|
||||||
|
public IXamlType TargetType { get; }
|
||||||
|
|
||||||
|
public RobustNameScopeRegistrationXamlIlNode(IXamlAstValueNode name, IXamlType targetType) : base(name)
|
||||||
|
{
|
||||||
|
TargetType = targetType;
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void VisitChildren(IXamlAstVisitor visitor)
|
||||||
|
=> Name = (IXamlAstValueNode)Name.Visit(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Emitter : IXamlAstLocalsNodeEmitter<IXamlILEmitter, XamlILNodeEmitResult>
|
||||||
|
{
|
||||||
|
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
|
||||||
|
{
|
||||||
|
if (node is RobustNameScopeRegistrationXamlIlNode registration)
|
||||||
|
{
|
||||||
|
|
||||||
|
var scopeField = context.RuntimeContext.ContextType.Fields.First(f =>
|
||||||
|
f.Name == XamlCompiler.ContextNameScopeFieldName);
|
||||||
|
var namescopeRegisterFunction = context.Configuration.TypeSystem
|
||||||
|
.FindType("Robust.Client.UserInterface.XAML.NameScope").Methods
|
||||||
|
.First(m => m.Name == "Register");
|
||||||
|
|
||||||
|
using (var targetLoc = context.GetLocalOfType(context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control")))
|
||||||
|
{
|
||||||
|
|
||||||
|
codeGen
|
||||||
|
// var target = {pop}
|
||||||
|
.Stloc(targetLoc.Local)
|
||||||
|
// _context.NameScope.Register(Name, target)
|
||||||
|
.Ldloc(context.ContextLocal)
|
||||||
|
.Ldfld(scopeField);
|
||||||
|
|
||||||
|
context.Emit(registration.Name, codeGen, registration.Name.Type.GetClrType());
|
||||||
|
|
||||||
|
codeGen
|
||||||
|
.Ldloc(targetLoc.Local)
|
||||||
|
.EmitCall(namescopeRegisterFunction, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return XamlILNodeEmitResult.Void(1);
|
||||||
|
}
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RobustMarkRootObjectScopeNode : IXamlAstTransformer
|
||||||
|
{
|
||||||
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
|
||||||
|
{
|
||||||
|
if (!context.ParentNodes().Any()
|
||||||
|
&& node is XamlValueWithManipulationNode mnode)
|
||||||
|
{
|
||||||
|
mnode.Manipulation = new XamlManipulationGroupNode(mnode,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
mnode.Manipulation,
|
||||||
|
new HandleRootObjectScopeNode(mnode)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
class HandleRootObjectScopeNode : XamlAstNode, IXamlAstManipulationNode
|
||||||
|
{
|
||||||
|
public HandleRootObjectScopeNode(IXamlLineInfo lineInfo) : base(lineInfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal class Emitter : IXamlILAstNodeEmitter
|
||||||
|
{
|
||||||
|
public XamlILNodeEmitResult Emit(IXamlAstNode node, XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
|
||||||
|
{
|
||||||
|
if (!(node is HandleRootObjectScopeNode))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var controlType = context.Configuration.TypeSystem.FindType("Robust.Client.UserInterface.Control");
|
||||||
|
|
||||||
|
var next = codeGen.DefineLabel();
|
||||||
|
var dontAbsorb = codeGen.DefineLabel();
|
||||||
|
var end = codeGen.DefineLabel();
|
||||||
|
var contextScopeField = context.RuntimeContext.ContextType.Fields.First(f =>
|
||||||
|
f.Name == XamlCompiler.ContextNameScopeFieldName);
|
||||||
|
var controlNameScopeField = controlType.Fields.First(f => f.Name == "NameScope");
|
||||||
|
var nameScopeType = context.Configuration.TypeSystem
|
||||||
|
.FindType("Robust.Client.UserInterface.XAML.NameScope");
|
||||||
|
var nameScopeCompleteMethod = nameScopeType.Methods.First(m => m.Name == "Complete");
|
||||||
|
var nameScopeAbsorbMethod = nameScopeType.Methods.First(m => m.Name == "Absorb");
|
||||||
|
using (var local = codeGen.LocalsPool.GetLocal(controlType))
|
||||||
|
{
|
||||||
|
codeGen
|
||||||
|
.Isinst(controlType)
|
||||||
|
.Dup()
|
||||||
|
.Stloc(local.Local) //store control in local field
|
||||||
|
.Brfalse(next) //if control is null, move to next (this should never happen but whatev, avalonia does it)
|
||||||
|
.Ldloc(context.ContextLocal)
|
||||||
|
.Ldfld(contextScopeField)
|
||||||
|
.Ldloc(local.Local) //load control from local field
|
||||||
|
.Ldfld(controlNameScopeField) //load namescope field from control
|
||||||
|
.EmitCall(nameScopeAbsorbMethod, true)
|
||||||
|
.Ldloc(local.Local) //load control
|
||||||
|
.Ldloc(context.ContextLocal) //load contextObject
|
||||||
|
.Ldfld(contextScopeField) //load namescope field from context obj
|
||||||
|
.Stfld(controlNameScopeField) //store namescope field in control
|
||||||
|
.MarkLabel(next)
|
||||||
|
.Ldloc(context.ContextLocal)
|
||||||
|
.Ldfld(contextScopeField)
|
||||||
|
.EmitCall(nameScopeCompleteMethod, true); //set the namescope as complete
|
||||||
|
}
|
||||||
|
|
||||||
|
return XamlILNodeEmitResult.Void(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
Robust.Client.Injectors/XamlCompiler.Helpers.cs
Normal file
78
Robust.Client.Injectors/XamlCompiler.Helpers.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Collections.Generic;
|
||||||
|
using XamlX.TypeSystem;
|
||||||
|
|
||||||
|
namespace Robust.Build.Tasks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helpers taken from:
|
||||||
|
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
|
||||||
|
/// - https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
|
||||||
|
/// </summary>
|
||||||
|
public partial class XamlCompiler
|
||||||
|
{
|
||||||
|
static bool CheckXamlName(IResource r) => r.Name.ToLowerInvariant().EndsWith(".xaml")
|
||||||
|
|| r.Name.ToLowerInvariant().EndsWith(".paml")
|
||||||
|
|| r.Name.ToLowerInvariant().EndsWith(".axaml");
|
||||||
|
|
||||||
|
private static bool MatchThisCall(Collection<Instruction> instructions, int idx)
|
||||||
|
{
|
||||||
|
var i = instructions[idx];
|
||||||
|
// A "normal" way of passing `this` to a static method:
|
||||||
|
|
||||||
|
// ldarg.0
|
||||||
|
// call void [Avalonia.Markup.Xaml]Avalonia.Markup.Xaml.AvaloniaXamlLoader::Load(object)
|
||||||
|
|
||||||
|
return i.OpCode == OpCodes.Ldarg_0 || (i.OpCode == OpCodes.Ldarg && i.Operand?.Equals(0) == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IResource : IFileSource
|
||||||
|
{
|
||||||
|
string Uri { get; }
|
||||||
|
string Name { get; }
|
||||||
|
void Remove();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IResourceGroup
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
IEnumerable<IResource> Resources { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmbeddedResources : IResourceGroup
|
||||||
|
{
|
||||||
|
private readonly AssemblyDefinition _asm;
|
||||||
|
public string Name => "EmbeddedResource";
|
||||||
|
|
||||||
|
public IEnumerable<IResource> Resources => _asm.MainModule.Resources.OfType<EmbeddedResource>()
|
||||||
|
.Select(r => new WrappedResource(_asm, r)).ToList();
|
||||||
|
|
||||||
|
public EmbeddedResources(AssemblyDefinition asm)
|
||||||
|
{
|
||||||
|
_asm = asm;
|
||||||
|
}
|
||||||
|
class WrappedResource : IResource
|
||||||
|
{
|
||||||
|
private readonly AssemblyDefinition _asm;
|
||||||
|
private readonly EmbeddedResource _res;
|
||||||
|
|
||||||
|
public WrappedResource(AssemblyDefinition asm, EmbeddedResource res)
|
||||||
|
{
|
||||||
|
_asm = asm;
|
||||||
|
_res = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Uri => $"resm:{Name}?assembly={_asm.Name.Name}";
|
||||||
|
public string Name => _res.Name;
|
||||||
|
public string FilePath => Name;
|
||||||
|
public byte[] FileContents => _res.GetResourceData();
|
||||||
|
|
||||||
|
public void Remove() => _asm.MainModule.Resources.Remove(_res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
317
Robust.Client.Injectors/XamlCompiler.cs
Normal file
317
Robust.Client.Injectors/XamlCompiler.cs
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Build.Framework;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Rocks;
|
||||||
|
using XamlX;
|
||||||
|
using XamlX.Ast;
|
||||||
|
using XamlX.Emit;
|
||||||
|
using XamlX.IL;
|
||||||
|
using XamlX.Parsers;
|
||||||
|
using XamlX.Transform;
|
||||||
|
using XamlX.TypeSystem;
|
||||||
|
|
||||||
|
namespace Robust.Build.Tasks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Based on https://github.com/AvaloniaUI/Avalonia/blob/c85fa2b9977d251a31886c2534613b4730fbaeaf/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
|
||||||
|
/// Adjusted for our UI-Framework
|
||||||
|
/// </summary>
|
||||||
|
public partial class XamlCompiler
|
||||||
|
{
|
||||||
|
public static (bool success, bool writtentofile) Compile(IBuildEngine engine, string input, string[] references,
|
||||||
|
string projectDirectory, string output, string strongNameKey)
|
||||||
|
{
|
||||||
|
var typeSystem = new CecilTypeSystem(references
|
||||||
|
.Where(r => !r.ToLowerInvariant().EndsWith("robust.build.tasks.dll"))
|
||||||
|
.Concat(new[] { input }), input);
|
||||||
|
|
||||||
|
var asm = typeSystem.TargetAssemblyDefinition;
|
||||||
|
|
||||||
|
var compileRes = CompileCore(engine, typeSystem);
|
||||||
|
if (compileRes == null)
|
||||||
|
return (true, false);
|
||||||
|
if (compileRes == false)
|
||||||
|
return (false, false);
|
||||||
|
|
||||||
|
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
|
||||||
|
if (!string.IsNullOrWhiteSpace(strongNameKey))
|
||||||
|
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
|
||||||
|
|
||||||
|
asm.Write(output, writerParameters);
|
||||||
|
|
||||||
|
return (true, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem)
|
||||||
|
{
|
||||||
|
var asm = typeSystem.TargetAssemblyDefinition;
|
||||||
|
var embrsc = new EmbeddedResources(asm);
|
||||||
|
|
||||||
|
if (embrsc.Resources.Count(CheckXamlName) == 0)
|
||||||
|
// Nothing to do
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var xamlLanguage = new XamlLanguageTypeMappings(typeSystem)
|
||||||
|
{
|
||||||
|
XmlnsAttributes =
|
||||||
|
{
|
||||||
|
typeSystem.GetType("Robust.Client.UserInterface.XAML.XmlnsDefinitionAttribute"),
|
||||||
|
|
||||||
|
},
|
||||||
|
ContentAttributes =
|
||||||
|
{
|
||||||
|
typeSystem.GetType("Robust.Client.UserInterface.XAML.ContentAttribute")
|
||||||
|
},
|
||||||
|
UsableDuringInitializationAttributes =
|
||||||
|
{
|
||||||
|
typeSystem.GetType("Robust.Client.UserInterface.XAML.UsableDuringInitializationAttribute")
|
||||||
|
},
|
||||||
|
DeferredContentPropertyAttributes =
|
||||||
|
{
|
||||||
|
typeSystem.GetType("Robust.Client.UserInterface.XAML.DeferredContentAttribute")
|
||||||
|
},
|
||||||
|
RootObjectProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestRootObjectProvider"),
|
||||||
|
UriContextProvider = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestUriContext"),
|
||||||
|
ProvideValueTarget = typeSystem.GetType("Robust.Client.UserInterface.XAML.ITestProvideValueTarget"),
|
||||||
|
};
|
||||||
|
var emitConfig = new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>
|
||||||
|
{
|
||||||
|
ContextTypeBuilderCallback = (b,c) => EmitNameScopeField(xamlLanguage, typeSystem, b, c)
|
||||||
|
};
|
||||||
|
|
||||||
|
var transformerconfig = new TransformerConfiguration(
|
||||||
|
typeSystem,
|
||||||
|
typeSystem.TargetAssembly,
|
||||||
|
xamlLanguage,
|
||||||
|
XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage));
|
||||||
|
|
||||||
|
var contextDef = new TypeDefinition("CompiledRobustXaml", "XamlIlContext",
|
||||||
|
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
|
||||||
|
asm.MainModule.Types.Add(contextDef);
|
||||||
|
var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
|
||||||
|
xamlLanguage, emitConfig);
|
||||||
|
|
||||||
|
var compiler =
|
||||||
|
new RobustXamlILCompiler(transformerconfig, emitConfig, true);
|
||||||
|
|
||||||
|
var loaderDispatcherDef = new TypeDefinition("CompiledRobustXaml", "!XamlLoader",
|
||||||
|
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
|
||||||
|
|
||||||
|
var loaderDispatcherMethod = new MethodDefinition("TryLoad",
|
||||||
|
MethodAttributes.Static | MethodAttributes.Public,
|
||||||
|
asm.MainModule.TypeSystem.Object)
|
||||||
|
{
|
||||||
|
Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)}
|
||||||
|
};
|
||||||
|
loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
|
||||||
|
asm.MainModule.Types.Add(loaderDispatcherDef);
|
||||||
|
|
||||||
|
var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
|
||||||
|
m =>
|
||||||
|
m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 2 &&
|
||||||
|
m.ReturnType.FullName == "System.Boolean"
|
||||||
|
&& m.Parameters[0].ParameterType.FullName == "System.String"
|
||||||
|
&& m.Parameters[1].ParameterType.FullName == "System.String"));
|
||||||
|
|
||||||
|
bool CompileGroup(IResourceGroup group)
|
||||||
|
{
|
||||||
|
var typeDef = new TypeDefinition("CompiledRobustXaml", "!" + group.Name, TypeAttributes.Class,
|
||||||
|
asm.MainModule.TypeSystem.Object);
|
||||||
|
|
||||||
|
//typeDef.CustomAttributes.Add(new CustomAttribute(ed));
|
||||||
|
asm.MainModule.Types.Add(typeDef);
|
||||||
|
var builder = typeSystem.CreateTypeBuilder(typeDef);
|
||||||
|
|
||||||
|
foreach (var res in group.Resources.Where(CheckXamlName))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", MessageImportance.Low);
|
||||||
|
|
||||||
|
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
|
||||||
|
var parsed = XDocumentXamlParser.Parse(xaml);
|
||||||
|
|
||||||
|
var initialRoot = (XamlAstObjectNode) parsed.Root;
|
||||||
|
|
||||||
|
var classDirective = initialRoot.Children.OfType<XamlAstXmlDirective>()
|
||||||
|
.FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class");
|
||||||
|
string classname;
|
||||||
|
if (classDirective != null && classDirective.Values[0] is XamlAstTextNode tn)
|
||||||
|
{
|
||||||
|
classname = tn.Text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
classname = res.Name.Replace(".xaml","");
|
||||||
|
}
|
||||||
|
|
||||||
|
var classType = typeSystem.TargetAssembly.FindType(classname);
|
||||||
|
if (classType == null)
|
||||||
|
throw new Exception($"Unable to find type '{classname}'");
|
||||||
|
|
||||||
|
compiler.Transform(parsed);
|
||||||
|
|
||||||
|
var populateName = $"Populate:{res.Name}";
|
||||||
|
var buildName = $"Build:{res.Name}";
|
||||||
|
|
||||||
|
var classTypeDefinition = typeSystem.GetTypeReference(classType).Resolve();
|
||||||
|
|
||||||
|
var populateBuilder = typeSystem.CreateTypeBuilder(classTypeDefinition);
|
||||||
|
|
||||||
|
compiler.Compile(parsed, contextClass,
|
||||||
|
compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
|
||||||
|
classTypeDefinition == null),
|
||||||
|
compiler.DefineBuildMethod(builder, parsed, buildName, true),
|
||||||
|
null,
|
||||||
|
(closureName, closureBaseType) =>
|
||||||
|
populateBuilder.DefineSubType(closureBaseType, closureName, false),
|
||||||
|
res.Uri, res
|
||||||
|
);
|
||||||
|
|
||||||
|
//add compiled populate method
|
||||||
|
var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve().Methods
|
||||||
|
.First(m => m.Name == populateName);
|
||||||
|
|
||||||
|
const string TrampolineName = "!XamlIlPopulateTrampoline";
|
||||||
|
var trampoline = new MethodDefinition(TrampolineName,
|
||||||
|
MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
|
||||||
|
trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
|
||||||
|
classTypeDefinition.Methods.Add(trampoline);
|
||||||
|
|
||||||
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
||||||
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||||
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
|
||||||
|
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
|
||||||
|
var foundXamlLoader = false;
|
||||||
|
// Find RobustXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
|
||||||
|
foreach (var method in classTypeDefinition.Methods
|
||||||
|
.Where(m => !m.Attributes.HasFlag(MethodAttributes.Static)))
|
||||||
|
{
|
||||||
|
var i = method.Body.Instructions;
|
||||||
|
for (var c = 1; c < i.Count; c++)
|
||||||
|
{
|
||||||
|
if (i[c].OpCode == OpCodes.Call)
|
||||||
|
{
|
||||||
|
var op = i[c].Operand as MethodReference;
|
||||||
|
|
||||||
|
if (op != null
|
||||||
|
&& op.Name == TrampolineName)
|
||||||
|
{
|
||||||
|
foundXamlLoader = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op != null
|
||||||
|
&& op.Name == "Load"
|
||||||
|
&& op.Parameters.Count == 1
|
||||||
|
&& op.Parameters[0].ParameterType.FullName == "System.Object"
|
||||||
|
&& op.DeclaringType.FullName == "Robust.Client.UserInterface.XAML.RobustXamlLoader")
|
||||||
|
{
|
||||||
|
if (MatchThisCall(i, c - 1))
|
||||||
|
{
|
||||||
|
i[c].Operand = trampoline;
|
||||||
|
foundXamlLoader = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundXamlLoader)
|
||||||
|
{
|
||||||
|
var ctors = classTypeDefinition.GetConstructors()
|
||||||
|
.Where(c => !c.IsStatic).ToList();
|
||||||
|
// We can inject xaml loader into default constructor
|
||||||
|
if (ctors.Count == 1 && ctors[0].Body.Instructions.Count(o=>o.OpCode != OpCodes.Nop) == 3)
|
||||||
|
{
|
||||||
|
var i = ctors[0].Body.Instructions;
|
||||||
|
var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret));
|
||||||
|
i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline));
|
||||||
|
i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidProgramException(
|
||||||
|
$"No call to RobustXamlLoader.Load(this) call found anywhere in the type {classType.FullName} and type seems to have custom constructors.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//add compiled build method
|
||||||
|
var compiledBuildMethod = typeSystem.GetTypeReference(builder).Resolve().Methods
|
||||||
|
.First(m => m.Name == buildName);
|
||||||
|
var parameterlessCtor = classTypeDefinition.GetConstructors()
|
||||||
|
.FirstOrDefault(c => c.IsPublic && !c.IsStatic && !c.HasParameters);
|
||||||
|
|
||||||
|
if (compiledBuildMethod != null && parameterlessCtor != null)
|
||||||
|
{
|
||||||
|
var i = loaderDispatcherMethod.Body.Instructions;
|
||||||
|
var nop = Instruction.Create(OpCodes.Nop);
|
||||||
|
i.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||||
|
i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
|
||||||
|
i.Add(Instruction.Create(OpCodes.Call, stringEquals));
|
||||||
|
i.Add(Instruction.Create(OpCodes.Brfalse, nop));
|
||||||
|
if (parameterlessCtor != null)
|
||||||
|
i.Add(Instruction.Create(OpCodes.Newobj, parameterlessCtor));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
i.Add(nop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
engine.LogWarningEvent(new BuildWarningEventArgs("XAMLIL", "", res.Uri, 0, 0, 0, 0,
|
||||||
|
e.ToString(), "", "CompileRobustXaml"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (embrsc.Resources.Count(CheckXamlName) != 0)
|
||||||
|
{
|
||||||
|
if (!CompileGroup(embrsc))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
||||||
|
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const string ContextNameScopeFieldName = "RobustNameScope";
|
||||||
|
|
||||||
|
private static void EmitNameScopeField(XamlLanguageTypeMappings xamlLanguage, CecilTypeSystem typeSystem, IXamlTypeBuilder<IXamlILEmitter> typeBuilder, IXamlILEmitter constructor)
|
||||||
|
{
|
||||||
|
var nameScopeType = typeSystem.FindType("Robust.Client.UserInterface.XAML.NameScope");
|
||||||
|
var field = typeBuilder.DefineField(nameScopeType,
|
||||||
|
ContextNameScopeFieldName, true, false);
|
||||||
|
constructor
|
||||||
|
.Ldarg_0()
|
||||||
|
.Newobj(nameScopeType.GetConstructor())
|
||||||
|
.Stfld(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IResource : IFileSource
|
||||||
|
{
|
||||||
|
string Uri { get; }
|
||||||
|
string Name { get; }
|
||||||
|
void Remove();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IResourceGroup
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
IEnumerable<IResource> Resources { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Robust.Client.NameGenerator/NameReferenceSyntaxReceiver.cs
Normal file
21
Robust.Client.NameGenerator/NameReferenceSyntaxReceiver.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
namespace Robust.Client.NameGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Taken from https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/NameReferenceSyntaxReceiver.cs
|
||||||
|
/// </summary>
|
||||||
|
internal class NameReferenceSyntaxReceiver : ISyntaxReceiver
|
||||||
|
{
|
||||||
|
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new List<ClassDeclarationSyntax>();
|
||||||
|
|
||||||
|
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||||
|
{
|
||||||
|
if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax &&
|
||||||
|
classDeclarationSyntax.AttributeLists.Count > 0)
|
||||||
|
CandidateClasses.Add(classDeclarationSyntax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0-3.final" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.1" PrivateAssets="all" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Link="XamlX\filename" Include="../XamlX/src/XamlX/**/*.cs" />
|
||||||
|
<Compile Remove="../XamlX/src/XamlX/**/SreTypeSystem.cs" />
|
||||||
|
<Compile Remove="../XamlX/src/XamlX/obj/**" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
287
Robust.Client.NameGenerator/RoslynTypeSystem.cs
Normal file
287
Robust.Client.NameGenerator/RoslynTypeSystem.cs
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using XamlX.TypeSystem;
|
||||||
|
|
||||||
|
namespace Robust.Client.NameGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Taken from https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/Infrastructure/RoslynTypeSystem.cs
|
||||||
|
/// </summary>
|
||||||
|
public class RoslynTypeSystem : IXamlTypeSystem
|
||||||
|
{
|
||||||
|
private readonly List<IXamlAssembly> _assemblies = new List<IXamlAssembly>();
|
||||||
|
|
||||||
|
public RoslynTypeSystem(CSharpCompilation compilation)
|
||||||
|
{
|
||||||
|
_assemblies.Add(new RoslynAssembly(compilation.Assembly));
|
||||||
|
|
||||||
|
var assemblySymbols = compilation
|
||||||
|
.References
|
||||||
|
.Select(compilation.GetAssemblyOrModuleSymbol)
|
||||||
|
.OfType<IAssemblySymbol>()
|
||||||
|
.Select(assembly => new RoslynAssembly(assembly))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
_assemblies.AddRange(assemblySymbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlAssembly> Assemblies => _assemblies;
|
||||||
|
|
||||||
|
public IXamlAssembly FindAssembly(string substring) => _assemblies[0];
|
||||||
|
|
||||||
|
public IXamlType FindType(string name)
|
||||||
|
{
|
||||||
|
foreach (var assembly in _assemblies)
|
||||||
|
{
|
||||||
|
var type = assembly.FindType(name);
|
||||||
|
if (type != null)
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IXamlType FindType(string name, string assembly)
|
||||||
|
{
|
||||||
|
foreach (var assemblyInstance in _assemblies)
|
||||||
|
{
|
||||||
|
var type = assemblyInstance.FindType(name);
|
||||||
|
if (type != null)
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RoslynAssembly : IXamlAssembly
|
||||||
|
{
|
||||||
|
private readonly IAssemblySymbol _symbol;
|
||||||
|
|
||||||
|
public RoslynAssembly(IAssemblySymbol symbol) => _symbol = symbol;
|
||||||
|
|
||||||
|
public bool Equals(IXamlAssembly other) =>
|
||||||
|
other is RoslynAssembly roslynAssembly &&
|
||||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynAssembly._symbol);
|
||||||
|
|
||||||
|
public string Name => _symbol.Name;
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes =>
|
||||||
|
_symbol.GetAttributes()
|
||||||
|
.Select(data => new RoslynAttribute(data, this))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public IXamlType FindType(string fullName)
|
||||||
|
{
|
||||||
|
var type = _symbol.GetTypeByMetadataName(fullName);
|
||||||
|
return type is null ? null : new RoslynType(type, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RoslynAttribute : IXamlCustomAttribute
|
||||||
|
{
|
||||||
|
private readonly AttributeData _data;
|
||||||
|
private readonly RoslynAssembly _assembly;
|
||||||
|
|
||||||
|
public RoslynAttribute(AttributeData data, RoslynAssembly assembly)
|
||||||
|
{
|
||||||
|
_data = data;
|
||||||
|
_assembly = assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(IXamlCustomAttribute other) =>
|
||||||
|
other is RoslynAttribute attribute &&
|
||||||
|
_data == attribute._data;
|
||||||
|
|
||||||
|
public IXamlType Type => new RoslynType(_data.AttributeClass, _assembly);
|
||||||
|
|
||||||
|
public List<object> Parameters =>
|
||||||
|
_data.ConstructorArguments
|
||||||
|
.Select(argument => argument.Value)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public Dictionary<string, object> Properties =>
|
||||||
|
_data.NamedArguments.ToDictionary(
|
||||||
|
pair => pair.Key,
|
||||||
|
pair => pair.Value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RoslynType : IXamlType
|
||||||
|
{
|
||||||
|
private static readonly SymbolDisplayFormat SymbolDisplayFormat = new SymbolDisplayFormat(
|
||||||
|
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
|
||||||
|
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters |
|
||||||
|
SymbolDisplayGenericsOptions.IncludeTypeConstraints |
|
||||||
|
SymbolDisplayGenericsOptions.IncludeVariance);
|
||||||
|
|
||||||
|
private readonly RoslynAssembly _assembly;
|
||||||
|
private readonly INamedTypeSymbol _symbol;
|
||||||
|
|
||||||
|
public RoslynType(INamedTypeSymbol symbol, RoslynAssembly assembly)
|
||||||
|
{
|
||||||
|
_symbol = symbol;
|
||||||
|
_assembly = assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(IXamlType other) =>
|
||||||
|
other is RoslynType roslynType &&
|
||||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynType._symbol);
|
||||||
|
|
||||||
|
public object Id => _symbol;
|
||||||
|
|
||||||
|
public string Name => _symbol.Name;
|
||||||
|
|
||||||
|
public string Namespace => _symbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat);
|
||||||
|
|
||||||
|
public string FullName => $"{Namespace}.{Name}";
|
||||||
|
|
||||||
|
public IXamlAssembly Assembly => _assembly;
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlProperty> Properties =>
|
||||||
|
_symbol.GetMembers()
|
||||||
|
.Where(member => member.Kind == SymbolKind.Property)
|
||||||
|
.OfType<IPropertySymbol>()
|
||||||
|
.Select(property => new RoslynProperty(property, _assembly))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlEventInfo> Events { get; } = new List<IXamlEventInfo>();
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlField> Fields { get; } = new List<IXamlField>();
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlMethod> Methods { get; } = new List<IXamlMethod>();
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlConstructor> Constructors =>
|
||||||
|
_symbol.Constructors
|
||||||
|
.Select(method => new RoslynConstructor(method, _assembly))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlType> GenericArguments { get; } = new List<IXamlType>();
|
||||||
|
|
||||||
|
public bool IsAssignableFrom(IXamlType type) => type == this;
|
||||||
|
|
||||||
|
public IXamlType MakeGenericType(IReadOnlyList<IXamlType> typeArguments) => this;
|
||||||
|
|
||||||
|
public IXamlType GenericTypeDefinition => this;
|
||||||
|
|
||||||
|
public bool IsArray => false;
|
||||||
|
|
||||||
|
public IXamlType ArrayElementType { get; } = null;
|
||||||
|
|
||||||
|
public IXamlType MakeArrayType(int dimensions) => null;
|
||||||
|
|
||||||
|
public IXamlType BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly);
|
||||||
|
|
||||||
|
public bool IsValueType { get; } = false;
|
||||||
|
|
||||||
|
public bool IsEnum { get; } = false;
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlType> Interfaces =>
|
||||||
|
_symbol.AllInterfaces
|
||||||
|
.Select(abstraction => new RoslynType(abstraction, _assembly))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public bool IsInterface => _symbol.IsAbstract;
|
||||||
|
|
||||||
|
public IXamlType GetEnumUnderlyingType() => null;
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlType> GenericParameters { get; } = new List<IXamlType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RoslynConstructor : IXamlConstructor
|
||||||
|
{
|
||||||
|
private readonly IMethodSymbol _symbol;
|
||||||
|
private readonly RoslynAssembly _assembly;
|
||||||
|
|
||||||
|
public RoslynConstructor(IMethodSymbol symbol, RoslynAssembly assembly)
|
||||||
|
{
|
||||||
|
_symbol = symbol;
|
||||||
|
_assembly = assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(IXamlConstructor other) =>
|
||||||
|
other is RoslynConstructor roslynConstructor &&
|
||||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynConstructor._symbol);
|
||||||
|
|
||||||
|
public bool IsPublic => true;
|
||||||
|
|
||||||
|
public bool IsStatic => false;
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlType> Parameters =>
|
||||||
|
_symbol.Parameters
|
||||||
|
.Select(parameter => parameter.Type)
|
||||||
|
.OfType<INamedTypeSymbol>()
|
||||||
|
.Select(type => new RoslynType(type, _assembly))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RoslynProperty : IXamlProperty
|
||||||
|
{
|
||||||
|
private readonly IPropertySymbol _symbol;
|
||||||
|
private readonly RoslynAssembly _assembly;
|
||||||
|
|
||||||
|
public RoslynProperty(IPropertySymbol symbol, RoslynAssembly assembly)
|
||||||
|
{
|
||||||
|
_symbol = symbol;
|
||||||
|
_assembly = assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(IXamlProperty other) =>
|
||||||
|
other is RoslynProperty roslynProperty &&
|
||||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynProperty._symbol);
|
||||||
|
|
||||||
|
public string Name => _symbol.Name;
|
||||||
|
|
||||||
|
public IXamlType PropertyType =>
|
||||||
|
_symbol.Type is INamedTypeSymbol namedTypeSymbol
|
||||||
|
? new RoslynType(namedTypeSymbol, _assembly)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
public IXamlMethod Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly);
|
||||||
|
|
||||||
|
public IXamlMethod Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly);
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlType> IndexerParameters { get; } = new List<IXamlType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RoslynMethod : IXamlMethod
|
||||||
|
{
|
||||||
|
private readonly IMethodSymbol _symbol;
|
||||||
|
private readonly RoslynAssembly _assembly;
|
||||||
|
|
||||||
|
public RoslynMethod(IMethodSymbol symbol, RoslynAssembly assembly)
|
||||||
|
{
|
||||||
|
_symbol = symbol;
|
||||||
|
_assembly = assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(IXamlMethod other) =>
|
||||||
|
other is RoslynMethod roslynMethod &&
|
||||||
|
SymbolEqualityComparer.Default.Equals(roslynMethod._symbol, _symbol);
|
||||||
|
|
||||||
|
public string Name => _symbol.Name;
|
||||||
|
|
||||||
|
public bool IsPublic => true;
|
||||||
|
|
||||||
|
public bool IsStatic => false;
|
||||||
|
|
||||||
|
public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly);
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlType> Parameters =>
|
||||||
|
_symbol.Parameters.Select(parameter => parameter.Type)
|
||||||
|
.OfType<INamedTypeSymbol>()
|
||||||
|
.Select(type => new RoslynType(type, _assembly))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType, _assembly);
|
||||||
|
|
||||||
|
public IXamlMethod MakeGenericMethod(IReadOnlyList<IXamlType> typeArguments) => null;
|
||||||
|
|
||||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>();
|
||||||
|
}
|
||||||
|
}
|
||||||
251
Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs
Normal file
251
Robust.Client.NameGenerator/XamlUiPartialClassGenerator.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
using XamlX.Ast;
|
||||||
|
using XamlX.Emit;
|
||||||
|
using XamlX.IL;
|
||||||
|
using XamlX.Parsers;
|
||||||
|
using XamlX.Transform;
|
||||||
|
using XamlX.Transform.Transformers;
|
||||||
|
using XamlX.TypeSystem;
|
||||||
|
|
||||||
|
namespace Robust.Client.NameGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Based on https://github.com/AvaloniaUI/Avalonia.NameGenerator/blob/ecc9677a23de5cbc90af07ccac14e31c0da41d6a/src/Avalonia.NameGenerator/NameReferenceGenerator.cs
|
||||||
|
/// Adjusted for our UI-Framework & needs.
|
||||||
|
/// </summary>
|
||||||
|
[Generator]
|
||||||
|
public class XamlUiPartialClassGenerator : ISourceGenerator
|
||||||
|
{
|
||||||
|
private const string AttributeName = "Robust.Client.AutoGenerated.GenerateTypedNameReferencesAttribute";
|
||||||
|
private const string AttributeFile = "GenerateTypedNameReferencesAttribute";
|
||||||
|
private const string AttributeCode = @"// <auto-generated />
|
||||||
|
using System;
|
||||||
|
namespace Robust.Client.AutoGenerated
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||||
|
public sealed class GenerateTypedNameReferencesAttribute : Attribute { }
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
class NameVisitor : IXamlAstVisitor
|
||||||
|
{
|
||||||
|
private List<(string name, string type)> _names = new List<(string name, string type)>();
|
||||||
|
|
||||||
|
public static List<(string name, string type)> GetNames(IXamlAstNode node)
|
||||||
|
{
|
||||||
|
var visitor = new NameVisitor();
|
||||||
|
node.Visit(visitor);
|
||||||
|
return visitor._names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsControl(IXamlType type) => type.FullName != "System.Object" &&
|
||||||
|
(type.FullName == "Robust.Client.UserInterface.Control" ||
|
||||||
|
IsControl(type.BaseType));
|
||||||
|
|
||||||
|
public IXamlAstNode Visit(IXamlAstNode node)
|
||||||
|
{
|
||||||
|
if (node is XamlAstObjectNode objectNode)
|
||||||
|
{
|
||||||
|
var clrtype = objectNode.Type.GetClrType();
|
||||||
|
var isControl = IsControl(clrtype);
|
||||||
|
//clrtype.Interfaces.Any(i =>
|
||||||
|
//i.IsInterface && i.FullName == "Robust.Client.UserInterface.IControl");
|
||||||
|
|
||||||
|
if (!isControl)
|
||||||
|
return node;
|
||||||
|
|
||||||
|
foreach (var child in objectNode.Children)
|
||||||
|
{
|
||||||
|
if (child is XamlAstXamlPropertyValueNode propertyValueNode &&
|
||||||
|
propertyValueNode.Property is XamlAstNamePropertyReference namedProperty &&
|
||||||
|
namedProperty.Name == "Name" &&
|
||||||
|
propertyValueNode.Values.Count > 0 &&
|
||||||
|
propertyValueNode.Values[0] is XamlAstTextNode text)
|
||||||
|
{
|
||||||
|
var reg = (text.Text, $@"{clrtype.Namespace}.{clrtype.Name}");
|
||||||
|
if (!_names.Contains(reg))
|
||||||
|
{
|
||||||
|
_names.Add(reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Push(IXamlAstNode node) { }
|
||||||
|
|
||||||
|
public void Pop() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateSourceCode(
|
||||||
|
INamedTypeSymbol classSymbol,
|
||||||
|
string xamlFile,
|
||||||
|
CSharpCompilation comp)
|
||||||
|
{
|
||||||
|
var className = classSymbol.Name;
|
||||||
|
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString();
|
||||||
|
var parsed = XDocumentXamlParser.Parse(xamlFile);
|
||||||
|
var typeSystem = new RoslynTypeSystem(comp);
|
||||||
|
var compiler =
|
||||||
|
new XamlILCompiler(
|
||||||
|
new TransformerConfiguration(typeSystem, typeSystem.Assemblies[0],
|
||||||
|
new XamlLanguageTypeMappings(typeSystem)),
|
||||||
|
new XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult>(), false);
|
||||||
|
compiler.Transformers.Add(new TypeReferenceResolver());
|
||||||
|
compiler.Transform(parsed);
|
||||||
|
var initialRoot = (XamlAstObjectNode) parsed.Root;
|
||||||
|
var names = NameVisitor.GetNames(initialRoot);
|
||||||
|
//var names = NameVisitor.GetNames((XamlAstObjectNode)XDocumentXamlParser.Parse(xamlFile).Root);
|
||||||
|
var namedControls = names.Select(info => " " +
|
||||||
|
$"protected global::{info.type} {info.name} => " +
|
||||||
|
$"this.FindControl<global::{info.type}>(\"{info.name}\");");
|
||||||
|
return $@"// <auto-generated />
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
namespace {nameSpace}
|
||||||
|
{{
|
||||||
|
partial class {className}
|
||||||
|
{{
|
||||||
|
{string.Join("\n", namedControls)}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Execute(GeneratorExecutionContext context)
|
||||||
|
{
|
||||||
|
var comp = (CSharpCompilation) context.Compilation;
|
||||||
|
if(comp.GetTypeByMetadataName(AttributeName) == null)
|
||||||
|
context.AddSource(AttributeFile, SourceText.From(AttributeCode, Encoding.UTF8));
|
||||||
|
if (!(context.SyntaxReceiver is NameReferenceSyntaxReceiver receiver))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var symbols = UnpackAnnotatedTypes(context, comp, receiver);
|
||||||
|
if(symbols == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var typeSymbol in symbols)
|
||||||
|
{
|
||||||
|
var xamlFileName = $"{typeSymbol.Name}.xaml";
|
||||||
|
var relevantXamlFile = context.AdditionalFiles.FirstOrDefault(t => t.Path.EndsWith(xamlFileName));
|
||||||
|
|
||||||
|
if (relevantXamlFile == null)
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(
|
||||||
|
Diagnostic.Create(
|
||||||
|
new DiagnosticDescriptor(
|
||||||
|
"RXN0001",
|
||||||
|
$"Unable to discover the relevant Robust XAML file for {typeSymbol}.",
|
||||||
|
"Unable to discover the relevant Robust XAML file " +
|
||||||
|
$"expected at {xamlFileName}",
|
||||||
|
"Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
true),
|
||||||
|
typeSymbol.Locations[0]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var txt = relevantXamlFile.GetText()?.ToString();
|
||||||
|
if (txt == null)
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(
|
||||||
|
Diagnostic.Create(
|
||||||
|
new DiagnosticDescriptor(
|
||||||
|
"RXN0002",
|
||||||
|
$"Unexpected empty Xaml-File was found at {xamlFileName}",
|
||||||
|
"Expected Content due to a Class with the same name being annotated with [GenerateTypedNameReferences].",
|
||||||
|
"Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
true),
|
||||||
|
Location.Create(xamlFileName, new TextSpan(0,0), new LinePositionSpan(new LinePosition(0,0),new LinePosition(0,0)))));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sourceCode = GenerateSourceCode(typeSymbol, txt, comp);
|
||||||
|
context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(
|
||||||
|
Diagnostic.Create(
|
||||||
|
new DiagnosticDescriptor(
|
||||||
|
"AXN0003",
|
||||||
|
"Unhandled exception occured while generating typed Name references.",
|
||||||
|
$"Unhandled exception occured while generating typed Name references: {e}",
|
||||||
|
"Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
true),
|
||||||
|
typeSymbol.Locations[0]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(in GeneratorExecutionContext context, CSharpCompilation comp, NameReferenceSyntaxReceiver receiver)
|
||||||
|
{
|
||||||
|
var options = (CSharpParseOptions) comp.SyntaxTrees[0].Options;
|
||||||
|
var compilation =
|
||||||
|
comp.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(AttributeCode, Encoding.UTF8), options));
|
||||||
|
var symbols = new List<INamedTypeSymbol>();
|
||||||
|
var attributeSymbol = compilation.GetTypeByMetadataName(AttributeName);
|
||||||
|
foreach (var candidateClass in receiver.CandidateClasses)
|
||||||
|
{
|
||||||
|
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
|
||||||
|
var typeSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(candidateClass);
|
||||||
|
var relevantAttribute = typeSymbol.GetAttributes().FirstOrDefault(attr =>
|
||||||
|
attr.AttributeClass != null &&
|
||||||
|
attr.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
|
||||||
|
|
||||||
|
if (relevantAttribute == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isPartial = candidateClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
|
||||||
|
|
||||||
|
if (isPartial)
|
||||||
|
{
|
||||||
|
symbols.Add(typeSymbol);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var missingPartialKeywordMessage =
|
||||||
|
$"The type {typeSymbol.Name} should be declared with the 'partial' keyword " +
|
||||||
|
"as it is annotated with the [GenerateTypedNameReferences] attribute.";
|
||||||
|
|
||||||
|
context.ReportDiagnostic(
|
||||||
|
Diagnostic.Create(
|
||||||
|
new DiagnosticDescriptor(
|
||||||
|
"RXN0004",
|
||||||
|
missingPartialKeywordMessage,
|
||||||
|
missingPartialKeywordMessage,
|
||||||
|
"Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
true),
|
||||||
|
Location.None));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(GeneratorInitializationContext context)
|
||||||
|
{
|
||||||
|
context.RegisterForSyntaxNotifications(() => new NameReferenceSyntaxReceiver());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,6 +115,7 @@ namespace Robust.Client
|
|||||||
IoCManager.Register<IViewVariablesManagerInternal, ViewVariablesManager>();
|
IoCManager.Register<IViewVariablesManagerInternal, ViewVariablesManager>();
|
||||||
IoCManager.Register<IClientConGroupController, ClientConGroupController>();
|
IoCManager.Register<IClientConGroupController, ClientConGroupController>();
|
||||||
IoCManager.Register<IScriptClient, ScriptClient>();
|
IoCManager.Register<IScriptClient, ScriptClient>();
|
||||||
|
//IoCManager.Register<IXamlCompiler, XamlCompiler>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,4 +45,10 @@
|
|||||||
|
|
||||||
<EmbeddedResource Include="Graphics\Clyde\Shaders\*" />
|
<EmbeddedResource Include="Graphics\Clyde\Shaders\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<RobustToolsPath>../Tools</RobustToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Target Name="RobustAfterBuild" AfterTargets="Build" />
|
||||||
|
<Import Project="..\MSBuild\XamlIL.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using JetBrains.Annotations;
|
|||||||
using Robust.Client.Graphics.Drawing;
|
using Robust.Client.Graphics.Drawing;
|
||||||
using Robust.Client.Interfaces.Graphics;
|
using Robust.Client.Interfaces.Graphics;
|
||||||
using Robust.Client.Interfaces.UserInterface;
|
using Robust.Client.Interfaces.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Animations;
|
using Robust.Shared.Animations;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
@@ -49,6 +50,45 @@ namespace Robust.Client.UserInterface
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public Control? Parent { get; private set; }
|
public Control? Parent { get; private set; }
|
||||||
|
|
||||||
|
public NameScope? NameScope;
|
||||||
|
|
||||||
|
//public void AttachNameScope(Dictionary<string, Control> nameScope)
|
||||||
|
//{
|
||||||
|
// _nameScope = nameScope;
|
||||||
|
//}
|
||||||
|
|
||||||
|
public NameScope? FindNameScope()
|
||||||
|
{
|
||||||
|
foreach (var control in this.GetSelfAndLogicalAncestors())
|
||||||
|
{
|
||||||
|
if (control.NameScope != null) return control.NameScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T FindControl<T>(string name) where T : Control
|
||||||
|
{
|
||||||
|
var nameScope = FindNameScope();
|
||||||
|
if (nameScope == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("No Namespace found for Control");
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = nameScope.Find(name);
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"No Control with the name {name} found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is not T ret)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Control with name {name} had invalid type {value.GetType()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
internal IUserInterfaceManagerInternal UserInterfaceManagerInternal { get; }
|
internal IUserInterfaceManagerInternal UserInterfaceManagerInternal { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -62,6 +102,9 @@ namespace Robust.Client.UserInterface
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public OrderedChildCollection Children { get; }
|
public OrderedChildCollection Children { get; }
|
||||||
|
|
||||||
|
[Content]
|
||||||
|
public virtual ICollection<Control> XamlChildren { get; protected set; }
|
||||||
|
|
||||||
[ViewVariables] public int ChildCount => _orderedChildren.Count;
|
[ViewVariables] public int ChildCount => _orderedChildren.Count;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -394,6 +437,7 @@ namespace Robust.Client.UserInterface
|
|||||||
UserInterfaceManagerInternal = IoCManager.Resolve<IUserInterfaceManagerInternal>();
|
UserInterfaceManagerInternal = IoCManager.Resolve<IUserInterfaceManagerInternal>();
|
||||||
StyleClasses = new StyleClassCollection(this);
|
StyleClasses = new StyleClassCollection(this);
|
||||||
Children = new OrderedChildCollection(this);
|
Children = new OrderedChildCollection(this);
|
||||||
|
XamlChildren = Children;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
15
Robust.Client/UserInterface/CustomControls/SS14Window.xaml
Normal file
15
Robust.Client/UserInterface/CustomControls/SS14Window.xaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<cc:SS14Window xmlns:cc="clr-namespace:Robust.Client.UserInterface.CustomControls"
|
||||||
|
xmlns:c="clr-namespace:Robust.Client.UserInterface.Controls">
|
||||||
|
<c:PanelContainer StyleClasses="windowPanel"/>
|
||||||
|
<c:VBoxContainer SeparationOverride="0">
|
||||||
|
<c:PanelContainer Name="WindowHeader" StyleClasses="windowHeader">
|
||||||
|
<c:HBoxContainer>
|
||||||
|
<c:MarginContainer MarginLeftOverride="5" SizeFlagsHorizontal="FillExpand">
|
||||||
|
<c:Label Name="TitleLabel" StyleIdentifier="foo" ClipText="True" Text="Exemplary Window Title Here" VAlign="Center" StyleClasses="windowTitle"/>
|
||||||
|
</c:MarginContainer>
|
||||||
|
<c:TextureButton Name="CloseButton" StyleClasses="windowCloseButton" SizeFlagsVertical="ShrinkCenter"></c:TextureButton>
|
||||||
|
</c:HBoxContainer>
|
||||||
|
</c:PanelContainer>
|
||||||
|
<c:MarginContainer Name="ContentsContainer" MarginBottomOverride="10" MarginLeftOverride="10" MarginRightOverride="10" MarginTopOverride="10" RectClipContent="True" SizeFlagsVertical="FillExpand" />
|
||||||
|
</c:VBoxContainer>
|
||||||
|
</cc:SS14Window>
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
using System;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Robust.Client.UserInterface.CustomControls
|
namespace Robust.Client.UserInterface.CustomControls
|
||||||
{
|
{
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public class SS14Window : BaseWindow
|
public partial class SS14Window : BaseWindow
|
||||||
{
|
{
|
||||||
public const string StyleClassWindowTitle = "windowTitle";
|
public const string StyleClassWindowTitle = "windowTitle";
|
||||||
public const string StyleClassWindowPanel = "windowPanel";
|
public const string StyleClassWindowPanel = "windowPanel";
|
||||||
@@ -17,70 +22,19 @@ namespace Robust.Client.UserInterface.CustomControls
|
|||||||
|
|
||||||
public SS14Window()
|
public SS14Window()
|
||||||
{
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
MouseFilter = MouseFilterMode.Stop;
|
MouseFilter = MouseFilterMode.Stop;
|
||||||
|
|
||||||
AddChild(new PanelContainer
|
WindowHeader.CustomMinimumSize = (0, HEADER_SIZE_Y);
|
||||||
{
|
|
||||||
StyleClasses = {StyleClassWindowPanel}
|
|
||||||
});
|
|
||||||
|
|
||||||
AddChild(new VBoxContainer
|
Contents = ContentsContainer;
|
||||||
{
|
|
||||||
SeparationOverride = 0,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new PanelContainer
|
|
||||||
{
|
|
||||||
StyleClasses = {StyleClassWindowHeader},
|
|
||||||
CustomMinimumSize = (0, HEADER_SIZE_Y),
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new HBoxContainer
|
|
||||||
{
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new MarginContainer
|
|
||||||
{
|
|
||||||
MarginLeftOverride = 5,
|
|
||||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
(TitleLabel = new Label
|
|
||||||
{
|
|
||||||
StyleIdentifier = "foo",
|
|
||||||
ClipText = true,
|
|
||||||
Text = "Exemplary Window Title Here",
|
|
||||||
VAlign = Label.VAlignMode.Center,
|
|
||||||
StyleClasses = {StyleClassWindowTitle}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(CloseButton = new TextureButton
|
|
||||||
{
|
|
||||||
StyleClasses = {StyleClassWindowCloseButton},
|
|
||||||
SizeFlagsVertical = SizeFlags.ShrinkCenter
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(Contents = new MarginContainer
|
|
||||||
{
|
|
||||||
MarginBottomOverride = 10,
|
|
||||||
MarginLeftOverride = 10,
|
|
||||||
MarginRightOverride = 10,
|
|
||||||
MarginTopOverride = 10,
|
|
||||||
RectClipContent = true,
|
|
||||||
SizeFlagsVertical = SizeFlags.FillExpand
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
CloseButton.OnPressed += CloseButtonPressed;
|
CloseButton.OnPressed += CloseButtonPressed;
|
||||||
|
XamlChildren = new SS14ContentCollection(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MarginContainer Contents { get; private set; }
|
public MarginContainer Contents { get; private set; }
|
||||||
private TextureButton CloseButton;
|
//private TextureButton CloseButton;
|
||||||
|
|
||||||
private const int DRAG_MARGIN_SIZE = 7;
|
private const int DRAG_MARGIN_SIZE = 7;
|
||||||
|
|
||||||
@@ -103,7 +57,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label TitleLabel;
|
//private Label TitleLabel;
|
||||||
|
|
||||||
public string? Title
|
public string? Title
|
||||||
{
|
{
|
||||||
@@ -177,5 +131,91 @@ namespace Robust.Client.UserInterface.CustomControls
|
|||||||
|
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SS14ContentCollection : ICollection<Control>, IReadOnlyCollection<Control>
|
||||||
|
{
|
||||||
|
private readonly SS14Window Owner;
|
||||||
|
|
||||||
|
public SS14ContentCollection(SS14Window owner)
|
||||||
|
{
|
||||||
|
Owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return new(Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<Control> IEnumerable<Control>.GetEnumerator() => GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public void Add(Control item)
|
||||||
|
{
|
||||||
|
Owner.Contents.AddChild(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Owner.Contents.RemoveAllChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(Control item)
|
||||||
|
{
|
||||||
|
return item?.Parent == Owner.Contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Control[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
Owner.Contents.Children.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(Control item)
|
||||||
|
{
|
||||||
|
if (item?.Parent != Owner.Contents)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTools.AssertNotNull(Owner?.Contents);
|
||||||
|
Owner!.Contents.RemoveChild(item);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ICollection<Control>.Count => Owner.Contents.ChildCount;
|
||||||
|
int IReadOnlyCollection<Control>.Count => Owner.Contents.ChildCount;
|
||||||
|
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<Control>
|
||||||
|
{
|
||||||
|
private OrderedChildCollection.Enumerator _enumerator;
|
||||||
|
|
||||||
|
internal Enumerator(SS14Window ss14Window)
|
||||||
|
{
|
||||||
|
_enumerator = ss14Window.Contents.Children.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
return _enumerator.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
((IEnumerator) _enumerator).Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Control Current => _enumerator.Current;
|
||||||
|
|
||||||
|
object IEnumerator.Current => Current;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_enumerator.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
17
Robust.Client/UserInterface/LogicalExtensions.cs
Normal file
17
Robust.Client/UserInterface/LogicalExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Robust.Client.UserInterface
|
||||||
|
{
|
||||||
|
public static class LogicalExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<Control> GetSelfAndLogicalAncestors(this Control control)
|
||||||
|
{
|
||||||
|
Control? c = control;
|
||||||
|
while (c != null)
|
||||||
|
{
|
||||||
|
yield return c;
|
||||||
|
c = c.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Robust.Client/UserInterface/XAML/Attributes.cs
Normal file
42
Robust.Client/UserInterface/XAML/Attributes.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Robust.Client.UserInterface.XAML
|
||||||
|
{
|
||||||
|
public class ContentAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class XmlnsDefinitionAttribute : Attribute
|
||||||
|
{
|
||||||
|
public XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UsableDuringInitializationAttribute : Attribute
|
||||||
|
{
|
||||||
|
public UsableDuringInitializationAttribute(bool usable)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DeferredContentAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITestRootObjectProvider
|
||||||
|
{
|
||||||
|
object RootObject { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITestProvideValueTarget
|
||||||
|
{
|
||||||
|
object TargetObject { get; }
|
||||||
|
object TargetProperty { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITestUriContext
|
||||||
|
{
|
||||||
|
Uri BaseUri { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
72
Robust.Client/UserInterface/XAML/NameScope.cs
Normal file
72
Robust.Client/UserInterface/XAML/NameScope.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Robust.Client.UserInterface.XAML
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements a name scope.
|
||||||
|
/// </summary>
|
||||||
|
public class NameScope
|
||||||
|
{
|
||||||
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
|
private readonly Dictionary<string, Control> _inner = new Dictionary<string, Control>();
|
||||||
|
|
||||||
|
public void Register(string name, Control element)
|
||||||
|
{
|
||||||
|
if (IsCompleted)
|
||||||
|
throw new InvalidOperationException("NameScope is completed, no further registrations are allowed");
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
if (element == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_inner.TryGetValue(name, out Control? existing))
|
||||||
|
{
|
||||||
|
if (existing != element)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Control with the name '{name}' already registered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_inner.Add(name, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Absorb(NameScope? nameScope)
|
||||||
|
{
|
||||||
|
if (nameScope == null) return;
|
||||||
|
|
||||||
|
foreach (var (name, control) in nameScope._inner)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Register(name, control);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Exception occured when trying to absorb NameScope (at name {name})", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Control? Find(string name)
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
|
||||||
|
_inner.TryGetValue(name, out var result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
IsCompleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Robust.Client/UserInterface/XAML/RobustXamlLoader.cs
Normal file
13
Robust.Client/UserInterface/XAML/RobustXamlLoader.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Robust.Client.UserInterface.XAML
|
||||||
|
{
|
||||||
|
public class RobustXamlLoader
|
||||||
|
{
|
||||||
|
public static void Load(object obj)
|
||||||
|
{
|
||||||
|
throw new Exception(
|
||||||
|
$"No precompiled XAML found for {obj.GetType()}, make sure to specify Class or name your class the same as your .xaml ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -260,6 +260,11 @@ namespace Robust.Shared.ContentPack
|
|||||||
// See II.14.2 in ECMA-335.
|
// See II.14.2 in ECMA-335.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case MTypeDefined:
|
||||||
|
{
|
||||||
|
// Valid for this to show up, safe to ignore.
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
@@ -471,6 +476,20 @@ namespace Robust.Shared.ContentPack
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case HandleKind.TypeDefinition:
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
parent = GetTypeFromDefinition(reader, (TypeDefinitionHandle) memRef.Parent);
|
||||||
|
}
|
||||||
|
catch (UnsupportedMetadataException u)
|
||||||
|
{
|
||||||
|
errors.Add(new SandboxError(u));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case HandleKind.TypeSpecification:
|
case HandleKind.TypeSpecification:
|
||||||
{
|
{
|
||||||
var typeSpec = reader.GetTypeSpecification((TypeSpecificationHandle) memRef.Parent);
|
var typeSpec = reader.GetTypeSpecification((TypeSpecificationHandle) memRef.Parent);
|
||||||
@@ -501,7 +520,7 @@ namespace Robust.Shared.ContentPack
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
errors.Add(new SandboxError(
|
errors.Add(new SandboxError(
|
||||||
$"Unsupported member ref parent type: {memRef.Parent}. Name: {memName}"));
|
$"Unsupported member ref parent type: {memRef.Parent.Kind}. Name: {memName}"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,6 +297,12 @@ Types:
|
|||||||
IEnumerable: { All: True }
|
IEnumerable: { All: True }
|
||||||
IEnumerator: { All: True }
|
IEnumerator: { All: True }
|
||||||
IReadOnlyList`1: { All: True }
|
IReadOnlyList`1: { All: True }
|
||||||
|
System.ComponentModel:
|
||||||
|
PropertyDescriptor: { }
|
||||||
|
ISite: { All: True }
|
||||||
|
IComponent: { All: True }
|
||||||
|
IContainer: { All: True }
|
||||||
|
ITypeDescriptorContext: { All: True }
|
||||||
System.Diagnostics.CodeAnalysis:
|
System.Diagnostics.CodeAnalysis:
|
||||||
AllowNullAttribute: { All: True }
|
AllowNullAttribute: { All: True }
|
||||||
DisallowNullAttribute: { All: True }
|
DisallowNullAttribute: { All: True }
|
||||||
@@ -651,6 +657,7 @@ Types:
|
|||||||
CancellationTokenSource: { All: True }
|
CancellationTokenSource: { All: True }
|
||||||
Interlocked: { All: True }
|
Interlocked: { All: True }
|
||||||
System:
|
System:
|
||||||
|
IServiceProvider: { All: True }
|
||||||
Action: { All: True }
|
Action: { All: True }
|
||||||
Action`1: { All: True }
|
Action`1: { All: True }
|
||||||
Action`2: { All: True }
|
Action`2: { All: True }
|
||||||
@@ -1132,6 +1139,7 @@ Types:
|
|||||||
# Content should never touch that.
|
# Content should never touch that.
|
||||||
Methods:
|
Methods:
|
||||||
- "bool Equals(object)"
|
- "bool Equals(object)"
|
||||||
|
- "bool Equals(System.Type)"
|
||||||
- "bool get_ContainsGenericParameters()"
|
- "bool get_ContainsGenericParameters()"
|
||||||
- "bool get_HasElementType()"
|
- "bool get_HasElementType()"
|
||||||
- "bool get_IsAbstract()"
|
- "bool get_IsAbstract()"
|
||||||
@@ -1251,6 +1259,7 @@ Types:
|
|||||||
UInt32: { All: True }
|
UInt32: { All: True }
|
||||||
UInt64: { All: True }
|
UInt64: { All: True }
|
||||||
UIntPtr: { All: True }
|
UIntPtr: { All: True }
|
||||||
|
Uri: { All: True }
|
||||||
ValueTuple: { All: True }
|
ValueTuple: { All: True }
|
||||||
ValueTuple`1: { All: True }
|
ValueTuple`1: { All: True }
|
||||||
ValueTuple`2: { All: True }
|
ValueTuple`2: { All: True }
|
||||||
|
|||||||
@@ -29,6 +29,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Robust.LoaderApi", "Robust.
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.LoaderApi", "Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj", "{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.LoaderApi", "Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj", "{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.Injectors", "Robust.Client.Injectors\Robust.Client.Injectors.csproj", "{EEF2C805-5E03-41EA-A916-49C1DD15EF41}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.NameGenerator", "Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj", "{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XamlX", "XamlX", "{1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX", "XamlX\src\XamlX\XamlX.csproj", "{D73768A2-BFCD-4916-8F52-4034C28F345C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.IL.Cecil", "XamlX\src\XamlX.IL.Cecil\XamlX.IL.Cecil.csproj", "{1CDC9C4F-668E-47A3-8A44-216E95644BEB}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.Runtime", "XamlX\src\XamlX.Runtime\XamlX.Runtime.csproj", "{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -125,6 +137,46 @@ Global
|
|||||||
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|Any CPU.Build.0 = Release|Any CPU
|
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|x64.ActiveCfg = Release|Any CPU
|
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|x64.Build.0 = Release|Any CPU
|
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{EEF2C805-5E03-41EA-A916-49C1DD15EF41}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -132,6 +184,9 @@ Global
|
|||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{ECBCE1D8-05C2-4881-9446-197C4C8E1C14} = {9143C8DD-A989-4089-9149-C50D12189FE4}
|
{ECBCE1D8-05C2-4881-9446-197C4C8E1C14} = {9143C8DD-A989-4089-9149-C50D12189FE4}
|
||||||
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15} = {805C8FD2-0C32-4DA8-BC4B-143BA5D48FF4}
|
{4FC5049F-AEEC-4DC0-9F4D-EB927AAB4F15} = {805C8FD2-0C32-4DA8-BC4B-143BA5D48FF4}
|
||||||
|
{D73768A2-BFCD-4916-8F52-4034C28F345C} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
|
||||||
|
{1CDC9C4F-668E-47A3-8A44-216E95644BEB} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
|
||||||
|
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {57757344-0FF4-4842-8A68-141CAA18A35D}
|
SolutionGuid = {57757344-0FF4-4842-8A68-141CAA18A35D}
|
||||||
|
|||||||
1
XamlX
Submodule
1
XamlX
Submodule
Submodule XamlX added at dca5a5f8c2
Reference in New Issue
Block a user