summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Gui/Markup/MarkupParser.cs
diff options
context:
space:
mode:
authorchai <215380520@qq.com>2024-06-03 10:15:45 +0800
committerchai <215380520@qq.com>2024-06-03 10:15:45 +0800
commitacea7b2e728787a0d83bbf83c8c1f042d2c32e7e (patch)
tree0bfec05c1ca2d71be2c337bcd110a0421f19318b /Plugins/MonoGame.Extended/source/MonoGame.Extended.Gui/Markup/MarkupParser.cs
parent88febcb02bf127d961c6471d9e846c0e1315f5c3 (diff)
+ plugins project
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Gui/Markup/MarkupParser.cs')
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Gui/Markup/MarkupParser.cs147
1 files changed, 147 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Gui/Markup/MarkupParser.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Gui/Markup/MarkupParser.cs
new file mode 100644
index 0000000..c2e8776
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Gui/Markup/MarkupParser.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Xml;
+using Microsoft.Xna.Framework;
+using MonoGame.Extended.Gui.Controls;
+
+namespace MonoGame.Extended.Gui.Markup
+{
+ public class MarkupParser
+ {
+ public MarkupParser()
+ {
+ }
+
+ private static readonly Dictionary<string, Type> _controlTypes =
+ typeof(Control).Assembly
+ .ExportedTypes
+ .Where(t => t.IsSubclassOf(typeof(Control)))
+ .ToDictionary(t => t.Name, StringComparer.OrdinalIgnoreCase);
+
+ private static readonly Dictionary<Type, Func<string, object>> _converters =
+ new Dictionary<Type, Func<string, object>>
+ {
+ {typeof(object), s => s},
+ {typeof(string), s => s},
+ {typeof(bool), s => bool.Parse(s)},
+ {typeof(int), s => int.Parse(s)},
+ {typeof(Color), s => s.StartsWith("#") ? ColorHelper.FromHex(s) : ColorHelper.FromName(s)}
+ };
+
+ private static object ConvertValue(Type propertyType, string input, object dataContext)
+ {
+ var value = ParseBinding(input, dataContext);
+
+ if (_converters.TryGetValue(propertyType, out var converter))
+ return converter(value); //property.SetValue(control, converter(value));
+
+ if (propertyType.IsEnum)
+ return
+ Enum.Parse(propertyType, value,
+ true); // property.SetValue(control, Enum.Parse(propertyType, value, true));
+
+ throw new InvalidOperationException($"Converter not found for {propertyType}");
+ }
+
+ private static object ParseChildNode(XmlNode node, Control parent, object dataContext)
+ {
+ if (node is XmlText)
+ return node.InnerText.Trim();
+
+ if (_controlTypes.TryGetValue(node.Name, out var type))
+ {
+ var typeInfo = type.GetTypeInfo();
+ var control = (Control) Activator.CreateInstance(type);
+
+ // ReSharper disable once AssignNullToNotNullAttribute
+ foreach (var attribute in node.Attributes.Cast<XmlAttribute>())
+ {
+ var property = typeInfo.GetProperty(attribute.Name);
+
+ if (property != null)
+ {
+ var value = ConvertValue(property.PropertyType, attribute.Value, dataContext);
+ property.SetValue(control, value);
+ }
+ else
+ {
+ var parts = attribute.Name.Split('.');
+ var parentType = parts[0];
+ var propertyName = parts[1];
+ var propertyType = parent.GetAttachedPropertyType(propertyName);
+ var propertyValue = ConvertValue(propertyType, attribute.Value, dataContext);
+
+ if (!string.Equals(parent.GetType().Name, parentType, StringComparison.OrdinalIgnoreCase))
+ throw new InvalidOperationException(
+ $"Attached properties are only supported on the immediate parent type {parentType}");
+
+ control.SetAttachedProperty(propertyName, propertyValue);
+ }
+ }
+
+
+ if (node.HasChildNodes)
+ {
+ switch (control)
+ {
+ case ContentControl contentControl:
+ if (node.ChildNodes.Count > 1)
+ throw new InvalidOperationException("A content control can only have one child");
+
+ contentControl.Content = ParseChildNode(node.ChildNodes[0], control, dataContext);
+ break;
+ case LayoutControl layoutControl:
+ foreach (var childControl in ParseChildNodes(node.ChildNodes, control, dataContext))
+ layoutControl.Items.Add(childControl as Control);
+ break;
+ }
+ }
+
+ return control;
+ }
+
+ throw new InvalidOperationException($"Unknown control type {node.Name}");
+ }
+
+ private static string ParseBinding(string expression, object dataContext)
+ {
+ if (dataContext != null && expression.StartsWith("{{") && expression.EndsWith("}}"))
+ {
+ var binding = expression.Substring(2, expression.Length - 4);
+ var bindingValue = dataContext
+ .GetType()
+ .GetProperty(binding)
+ ?.GetValue(dataContext);
+
+ return $"{bindingValue}";
+ }
+
+ return expression;
+ }
+
+ private static IEnumerable<object> ParseChildNodes(XmlNodeList nodes, Control parent, object dataContext)
+ {
+ foreach (var node in nodes.Cast<XmlNode>())
+ {
+ if (node.Name == "xml")
+ {
+ // TODO: Validate header
+ }
+ else
+ {
+ yield return ParseChildNode(node, parent, dataContext);
+ }
+ }
+ }
+
+ public Control Parse(string filePath, object dataContext)
+ {
+ var d = new XmlDocument();
+ d.Load(filePath);
+ return ParseChildNodes(d.ChildNodes, null, dataContext)
+ .LastOrDefault() as Control;
+ }
+ }
+}