Skip to content

Commit

Permalink
Add custom component binding generation support
Browse files Browse the repository at this point in the history
  • Loading branch information
Eastrall committed Aug 15, 2023
1 parent b7391f5 commit a2f679f
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 30 deletions.
67 changes: 45 additions & 22 deletions Editor/Scripts/Generator/Helpers/RosalinaStatementSyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

internal static class RosalinaStatementSyntaxFactory
{
public static InitializationStatement[] GenerateInitializeStatements(UxmlDocument uxmlDocument, MemberAccessExpressionSyntax documentQueryMethodAccess)
{
var statements = new List<InitializationStatement>();
IEnumerable<UIProperty> properties = uxmlDocument.GetChildren().Select(x => new UIProperty(x.Type, x.Name)).ToList();
IEnumerable<UIProperty> properties = uxmlDocument.GetChildren().Select(x => new UIProperty(x)).ToList();

if (CheckForDuplicateProperties(properties))
{
Expand All @@ -30,35 +31,57 @@ public static InitializationStatement[] GenerateInitializeStatements(UxmlDocumen

PropertyDeclarationSyntax @property = RosalinaSyntaxFactory.CreateProperty(uiProperty.Type.Name, uiProperty.Name, SyntaxKind.PublicKeyword)
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
)
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.AddModifiers(Token(SyntaxKind.PrivateKeyword))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
);

var argumentList = SyntaxFactory.SeparatedList(new[]
SeparatedSyntaxList<ArgumentSyntax> argumentList = SeparatedList(new[]
{
SyntaxFactory.Argument(
SyntaxFactory.LiteralExpression(
Argument(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(uiProperty.OriginalName)
Literal(uiProperty.OriginalName)
)
)
});
var cast = SyntaxFactory.CastExpression(
SyntaxFactory.ParseTypeName(uiProperty.Type.Name),
SyntaxFactory.InvocationExpression(documentQueryMethodAccess, SyntaxFactory.ArgumentList(argumentList))
);
var statement = SyntaxFactory.ExpressionStatement(
SyntaxFactory.AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
SyntaxFactory.IdentifierName(uiProperty.Name),
cast
)
);
InvocationExpressionSyntax queryElementMethodInvocation = InvocationExpression(documentQueryMethodAccess, ArgumentList(argumentList));
ExpressionStatementSyntax statement;

if (!uiProperty.IsCustomComponent)
{
statement = ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(uiProperty.Name),
CastExpression(
ParseTypeName(uiProperty.Type.Name),
queryElementMethodInvocation
)
)
);
}
else
{
statement = ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(uiProperty.Name),
ObjectCreationExpression(IdentifierName(uiProperty.Type.Name))
.WithArgumentList(
ArgumentList(
SingletonSeparatedList(
Argument(queryElementMethodInvocation)
)
)
)
)
);
}

statements.Add(new InitializationStatement(statement, property));
}
Expand Down
15 changes: 10 additions & 5 deletions Editor/Scripts/Generator/UIProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ internal readonly struct UIProperty
/// </summary>
public string OriginalName { get; }

public UIProperty(string type, string name)
/// <summary>
/// Gets a boolean value that indicates if the UI property represents a custom component.
/// </summary>
public bool IsCustomComponent => TypeName == "Instance";

public UIProperty(UxmlNode uxmlNode)
{
TypeName = type;
Type = UIPropertyTypes.GetUIElementType(type);
OriginalName = name;
Name = name.Contains('-') ? name.ToPascalCase() : OriginalName;
TypeName = uxmlNode.Type;
Type = UIPropertyTypes.GetUIElementType(uxmlNode.Type) ?? UIPropertyTypes.GetCustomUIElementType(uxmlNode.Template);
OriginalName = uxmlNode.Name;
Name = uxmlNode.Name.Contains('-') ? uxmlNode.Name.ToPascalCase() : OriginalName;
}
}

Expand Down
8 changes: 7 additions & 1 deletion Editor/Scripts/Generator/UIPropertyTypes.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;

internal static class UIPropertyTypes
{
Expand All @@ -26,13 +27,18 @@ internal static class UIPropertyTypes
{ "DropdownField", typeof(UnityEngine.UIElements.DropdownField) },
{ "RadioButton", typeof(UnityEngine.UIElements.RadioButton) },
{ "RadioButtonGroup", typeof(UnityEngine.UIElements.RadioButtonGroup) },
{ "Image", typeof(UnityEngine.UIElements.Image) },
{ "Image", typeof(UnityEngine.UIElements.Image) }
};

public static Type GetUIElementType(string uiElementName)
{
return _nativeUITypes.TryGetValue(uiElementName, out Type type) ? type : null;
}

public static Type GetCustomUIElementType(string templateName)
{
return AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).FirstOrDefault(x => x.Name == templateName);
}
}

#endif
4 changes: 3 additions & 1 deletion Editor/Scripts/Parsing/RosalinaUXMLParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
internal class RosalinaUXMLParser
{
private const string ElementAttributeName = "name";
private const string ElementAttributeTemplateName = "template";
private const string EditorExtensionAttributeName = "editor-extension-mode";

/// <summary>
Expand All @@ -27,7 +28,8 @@ private static UxmlNode ParseUxmlNode(XElement xmlNode)
{
string type = xmlNode.Name.LocalName;
string name = xmlNode.Attribute(ElementAttributeName)?.Value ?? string.Empty;
var node = new UxmlNode(type, name, xmlNode.Parent is null);
string template = xmlNode.Attribute(ElementAttributeTemplateName)?.Value ?? string.Empty;
var node = new UxmlNode(type, name, xmlNode.Parent is null, template);

if (xmlNode.HasElements)
{
Expand Down
9 changes: 8 additions & 1 deletion Editor/Scripts/Parsing/UxmlNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ internal class UxmlNode
/// </summary>
public bool IsRoot { get; }

/// <summary>
/// Gets the UXML template name in case of custom component; null otherwise.
/// </summary>
public string Template { get; }

/// <summary>
/// Gets the UXML child nodes.
/// </summary>
Expand All @@ -36,11 +41,13 @@ internal class UxmlNode
/// <param name="type">Node type.</param>
/// <param name="name">Node name.</param>
/// <param name="isRoot">Is root node.</param>
public UxmlNode(string type, string name, bool isRoot = false)
/// <param name="template">Node template in case of custom component.</param>
public UxmlNode(string type, string name, bool isRoot = false, string template = null)
{
Type = type;
Name = name;
IsRoot = isRoot;
Template = template;
}
}
#endif

0 comments on commit a2f679f

Please sign in to comment.