Files
2023-05-31 20:28:52 -04:00

476 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Styling;
using Avalonia.Threading;
using AvaloniaEdit;
using AvaloniaEdit.Indentation.CSharp;
using FluentAvalonia.Core.Attributes;
using FluentAvalonia.UI.Controls;
namespace FluentAvaloniaSamples.Controls;
public class ControlDefinitionOverlay : TemplatedControl
{
public ControlDefinitionOverlay()
{
IsVisible = false;
}
public static readonly StyledProperty<Type> TargetTypeProperty =
AvaloniaProperty.Register<ControlDefinitionOverlay, Type>(nameof(TargetType));
public static readonly StyledProperty<string> InheritanceProperty =
AvaloniaProperty.Register<ControlDefinitionOverlay, string>(nameof(Inheritance));
public static readonly StyledProperty<string> PseudoclassesListProperty =
AvaloniaProperty.Register<ControlDefinitionOverlay, string>(nameof(PseudoclassesList));
public Type TargetType
{
get => GetValue(TargetTypeProperty);
set => SetValue(TargetTypeProperty, value);
}
public string Inheritance
{
get => GetValue(InheritanceProperty);
set => SetValue(InheritanceProperty, value);
}
public string PseudoclassesList
{
get => GetValue(PseudoclassesListProperty);
set => SetValue(PseudoclassesListProperty, value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_closeButton != null)
_closeButton.Click -= OnCloseClick;
base.OnApplyTemplate(e);
// _textEditor = e.NameScope.Find<TextEditor>("TextEditor");
//_textEditor.TextArea.IndentationStrategy = new CSharpIndentationStrategy();
//_textEditor.SyntaxHighlighting = CSharpHighlightingSource.CSharpDarkMode;
_closeButton = e.NameScope.Find<Button>("CloseButton");
_closeButton.Click += OnCloseClick;
Show();
}
public async void Show()
{
Inheritance = null;
PseudoclassesList = null;
//if (_textEditor != null)
// _textEditor.Text = null;
Opacity = 0;
IsVisible = true;
var ani = new Animation
{
Duration = TimeSpan.FromMilliseconds(250),
Children =
{
new KeyFrame
{
Cue = new Cue(0d),
Setters =
{
new Setter(OpacityProperty, 0d),
new Setter(ScaleTransform.ScaleXProperty, 1.05d),
new Setter(ScaleTransform.ScaleYProperty, 1.05d)
}
},
new KeyFrame
{
Cue = new Cue(1d),
Setters =
{
new Setter(OpacityProperty, 1d),
new Setter(ScaleTransform.ScaleXProperty, 1d),
new Setter(ScaleTransform.ScaleYProperty, 1d)
},
KeySpline = new KeySpline(0,0,0,1)
}
},
FillMode = FillMode.Forward
};
await ani.RunAsync(this);
await BuildControlDefinition(TargetType);
if (this.TryFindResource("TextControlSelectionHighlightColor", out var value))
{
if (value is ISolidColorBrush sb)
{
var b = new ImmutableSolidColorBrush(sb.Color, 0.5);
//_textEditor.TextArea.SelectionBrush = b;
}
}
}
public async void Hide()
{
var ani = new Animation
{
Duration = TimeSpan.FromMilliseconds(167),
Children =
{
new KeyFrame
{
Cue = new Cue(0d),
Setters =
{
new Setter(OpacityProperty, 1d),
new Setter(ScaleTransform.ScaleXProperty, 1d),
new Setter(ScaleTransform.ScaleYProperty, 1d)
}
},
new KeyFrame
{
Cue = new Cue(1d),
Setters =
{
new Setter(OpacityProperty, 0d),
new Setter(ScaleTransform.ScaleXProperty, 1.05d),
new Setter(ScaleTransform.ScaleYProperty, 1.05d)
},
KeySpline = new KeySpline(0,0,0,1)
}
},
FillMode = FillMode.Forward
};
await ani.RunAsync(this);
IsVisible = false;
}
public async Task<bool> BuildControlDefinition(Type t)
{
return await Task.Run(() =>
{
try
{
var inheritance = GetInheritance(t);
var ps = GetPseudoclasses(t);
var src = GeneratePseudoSource(t);
Dispatcher.UIThread.Post(() =>
{
Inheritance = inheritance;
PseudoclassesList = ps;
//_textEditor.Text = src;
//_textEditor.TextArea.IndentationStrategy.IndentLines(_textEditor.Document, 0, _textEditor.Document.LineCount);
}, DispatcherPriority.Background);
return true;
}
catch
{
return false;
}
});
}
private string GetInheritance(Type t)
{
var sb = new StringBuilder();
sb.Append(t.Name);
var cur = t.BaseType;
while (cur != null)
{
sb.Insert(0, " > ");
sb.Insert(0, cur.Name);
cur = cur.BaseType;
}
return sb.ToString();
}
private string GetPseudoclasses(Type t)
{
var pcAttr = t.GetCustomAttributes(typeof(PseudoClassesAttribute), true);
var sb = new StringBuilder();
foreach (PseudoClassesAttribute attr in pcAttr)
{
foreach (var pc in attr.PseudoClasses)
{
sb.Append(pc);
sb.Append(',');
}
}
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
private string GeneratePseudoSource(Type t)
{
var sb = new StringBuilder();
// Generate class name
sb.Append($"public class {t.Name} : {t.BaseType.Name}");
sb.Append("\n{\n");
// Ignore constructors, all controls have to have a public ctor
// AvaloniaProperties
sb.AppendLine("// Avalonia Properties");
var avProps = AvaloniaPropertyRegistry.Instance.GetRegistered(t)
.Where(x => x.OwnerType == t);
foreach (var p in avProps)
{
if (p.IsDirect)
{
sb.AppendLine($"public static readonly DirectProperty<{p.OwnerType.Name}, {ResolveType(p.PropertyType)}> {p.Name}Property;");
}
else
{
sb.AppendLine($"public static readonly StyledProperty<{ResolveType(p.PropertyType)}> {p.Name}Property;");
}
}
sb.Append("\n");
// CLR properties
sb.AppendLine("// CLR Properties");
var clr = t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.DeclaringType == t);
// Keep track of enums used, we'll define them too at the end of the file
List<Type> enums = new List<Type>();
foreach (var p in clr)
{
if (p.GetCustomAttribute<NotImplementedAttribute>(true) != null)
{
sb.Append("[Not Implemented] ");
}
sb.Append($"public {ResolveType(p.PropertyType)} {p.Name}");
// I don't know of many set only properties so we're safe with this
sb.Append(" { get; ");
if (p.CanWrite)
{
sb.Append("set; }\n");
}
else
{
sb.Append("}\n");
}
if (p.PropertyType.IsEnum)
{
enums.Add(p.PropertyType);
}
}
sb.Append("\n");
// Public events
sb.AppendLine("// Events");
var events = t.GetEvents(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.DeclaringType == t);
foreach (var ev in events)
{
if (ev.GetCustomAttribute<NotImplementedAttribute>(true) != null)
{
sb.Append("[Not Implemented] ");
}
sb.Append("public event ");
sb.Append(ResolveType(ev.EventHandlerType));
sb.Append(' ');
sb.Append(ev.Name);
sb.Append(';');
sb.Append('\n');
}
sb.Append("\n");
// Public methods
sb.AppendLine("// Public Methods");
var methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.DeclaringType == t && !x.IsSpecialName && x.GetBaseDefinition() == x);
foreach (var m in methods)
{
if (m.GetCustomAttribute<NotImplementedAttribute>(true) != null)
{
sb.Append("[Not Implemented] ");
}
sb.Append("public ");
if (m.IsVirtual)
{
sb.Append("virtual ");
}
sb.Append(ResolveType(m.ReturnType));
sb.Append(' ');
sb.Append(m.Name);
sb.Append('(');
var args = m.GetParameters();
foreach (var arg in args)
{
sb.Append(ResolveType(arg.ParameterType));
sb.Append(' ');
sb.Append(arg.Name);
sb.Append(", ");
}
if (args.Length > 0)
{
sb.Remove(sb.Length - 2, 2);
}
sb.Append(')');
sb.Append(';');
sb.Append('\n');
}
sb.AppendLine("}\n");
// Now any enums used in the above class, let's define here
foreach (var en in enums)
{
// Skip the Symbol enum because it's too big and can be looked up on the SymbolIcon page
if (en == typeof(Symbol))
continue;
if (en.GetCustomAttribute<FlagsAttribute>() != null)
{
sb.Append("[Flags]\n");
}
sb.Append("public enum ");
sb.Append(en.Name);
sb.Append("\n{\n");
var names = Enum.GetNames(en);
for (int i = 0; i < names.Length; i++)
{
var fi = en.GetField(names[i]);
if (fi.GetCustomAttribute<ObsoleteAttribute>() != null)
{
sb.Append("[Obsolete] ");
}
sb.Append(names[i]);
sb.Append(' ');
sb.Append('=');
sb.Append(' ');
sb.Append(fi.GetRawConstantValue().ToString());
sb.Append(',');
sb.Append('\n');
}
sb.AppendLine("}\n");
}
return sb.ToString();
}
private string ResolveType(Type t)
{
if (t.IsGenericType)
{
var genType = t.GetGenericTypeDefinition().Name;
genType = genType.Substring(0, genType.IndexOf('`'));
var args = t.GetGenericArguments().Select(x => ResolveType(x)).ToArray();
if (genType.Equals("Nullable", StringComparison.OrdinalIgnoreCase))
{
return $"{args[0]}?";
}
else
{
return $"{genType}<{string.Join(',', args)}>";
}
}
else
{
if (t == typeof(object))
return "object";
else if (t == typeof(string))
return "string";
else if (t == typeof(bool))
return "bool";
else if (t == typeof(char))
return "char";
else if (t == typeof(int))
return "int";
else if (t == typeof(float))
return "float";
else if (t == typeof(double))
return "double";
else if (t == typeof(long))
return "long";
else if (t == typeof(ulong))
return "ulong";
else if (t == typeof(uint))
return "uint";
else if (t == typeof(byte))
return "byte";
else if (t == typeof(short))
return "short";
else if (t == typeof(decimal))
return "decimal";
else if (t == typeof(void))
return "void";
return t.Name;
}
}
private void OnCloseClick(object sender, RoutedEventArgs e)
{
Hide();
}
private Button _closeButton;
//private TextEditor _textEditor;
}