16 changed files with 365 additions and 49 deletions
@ -0,0 +1,26 @@ |
|||||
|
using Avalonia.Input; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Describes options used to customize DevTools.
|
||||
|
/// </summary>
|
||||
|
public class DevToolsOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the key gesture used to open DevTools.
|
||||
|
/// </summary>
|
||||
|
public KeyGesture Gesture { get; set; } = new KeyGesture(Key.F12); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a value indicating whether DevTools should be displayed as a child window
|
||||
|
/// of the window being inspected. The default value is true.
|
||||
|
/// </summary>
|
||||
|
public bool ShowAsChildWindow { get; set; } = true; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the initial size of the DevTools window. The default value is 1024x512.
|
||||
|
/// </summary>
|
||||
|
public Size Size { get; set; } = new Size(1024, 512); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Data; |
||||
|
|
||||
|
namespace Avalonia |
||||
|
{ |
||||
|
internal static class ClassBindingManager |
||||
|
{ |
||||
|
private static readonly Dictionary<string, AvaloniaProperty> s_RegisteredProperties = |
||||
|
new Dictionary<string, AvaloniaProperty>(); |
||||
|
|
||||
|
public static IDisposable Bind(IStyledElement target, string className, IBinding source, object anchor) |
||||
|
{ |
||||
|
if (!s_RegisteredProperties.TryGetValue(className, out var prop)) |
||||
|
s_RegisteredProperties[className] = prop = RegisterClassProxyProperty(className); |
||||
|
return target.Bind(prop, source, anchor); |
||||
|
} |
||||
|
|
||||
|
private static AvaloniaProperty RegisterClassProxyProperty(string className) |
||||
|
{ |
||||
|
var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className); |
||||
|
prop.Changed.Subscribe(args => |
||||
|
{ |
||||
|
var classes = ((IStyledElement)args.Sender).Classes; |
||||
|
classes.Set(className, args.NewValue.GetValueOrDefault()); |
||||
|
}); |
||||
|
|
||||
|
return prop; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Data; |
||||
|
|
||||
|
namespace Avalonia |
||||
|
{ |
||||
|
public static class StyledElementExtensions |
||||
|
{ |
||||
|
public static IDisposable BindClass(this IStyledElement target, string className, IBinding source, object anchor) => |
||||
|
ClassBindingManager.Bind(target, className, source, anchor); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,97 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using XamlX.Ast; |
||||
|
using XamlX.Emit; |
||||
|
using XamlX.IL; |
||||
|
using XamlX.Transform; |
||||
|
using XamlX.TypeSystem; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
||||
|
{ |
||||
|
class AvaloniaXamlIlResolveClassesPropertiesTransformer : IXamlAstTransformer |
||||
|
{ |
||||
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) |
||||
|
{ |
||||
|
if (node is XamlAstNamePropertyReference prop |
||||
|
&& prop.TargetType is XamlAstClrTypeReference targetRef |
||||
|
&& prop.DeclaringType is XamlAstClrTypeReference declaringRef) |
||||
|
{ |
||||
|
var types = context.GetAvaloniaTypes(); |
||||
|
if (types.StyledElement.IsAssignableFrom(targetRef.Type) |
||||
|
&& types.Classes.Equals(declaringRef.Type)) |
||||
|
{ |
||||
|
return new XamlAstClrProperty(node, "class:" + prop.Name, types.Classes, |
||||
|
null) |
||||
|
{ |
||||
|
Setters = { new ClassValueSetter(types, prop.Name), new ClassBindingSetter(types, prop.Name) } |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
class ClassValueSetter : IXamlEmitablePropertySetter<IXamlILEmitter> |
||||
|
{ |
||||
|
private readonly AvaloniaXamlIlWellKnownTypes _types; |
||||
|
private readonly string _className; |
||||
|
|
||||
|
public ClassValueSetter(AvaloniaXamlIlWellKnownTypes types, string className) |
||||
|
{ |
||||
|
_types = types; |
||||
|
_className = className; |
||||
|
Parameters = new[] { types.XamlIlTypes.Boolean }; |
||||
|
} |
||||
|
|
||||
|
public void Emit(IXamlILEmitter emitter) |
||||
|
{ |
||||
|
using (var value = emitter.LocalsPool.GetLocal(_types.XamlIlTypes.Boolean)) |
||||
|
{ |
||||
|
emitter |
||||
|
.Stloc(value.Local) |
||||
|
.EmitCall(_types.StyledElementClassesProperty.Getter) |
||||
|
.Ldstr(_className) |
||||
|
.Ldloc(value.Local) |
||||
|
.EmitCall(_types.Classes.GetMethod(new FindMethodMethodSignature("Set", |
||||
|
_types.XamlIlTypes.Void, _types.XamlIlTypes.String, _types.XamlIlTypes.Boolean))); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IXamlType TargetType => _types.StyledElement; |
||||
|
|
||||
|
public PropertySetterBinderParameters BinderParameters { get; } = |
||||
|
new PropertySetterBinderParameters { AllowXNull = false }; |
||||
|
public IReadOnlyList<IXamlType> Parameters { get; } |
||||
|
} |
||||
|
|
||||
|
class ClassBindingSetter : IXamlEmitablePropertySetter<IXamlILEmitter> |
||||
|
{ |
||||
|
private readonly AvaloniaXamlIlWellKnownTypes _types; |
||||
|
private readonly string _className; |
||||
|
|
||||
|
public ClassBindingSetter(AvaloniaXamlIlWellKnownTypes types, string className) |
||||
|
{ |
||||
|
_types = types; |
||||
|
_className = className; |
||||
|
Parameters = new[] {types.IBinding}; |
||||
|
} |
||||
|
|
||||
|
public void Emit(IXamlILEmitter emitter) |
||||
|
{ |
||||
|
using (var bloc = emitter.LocalsPool.GetLocal(_types.IBinding)) |
||||
|
emitter |
||||
|
.Stloc(bloc.Local) |
||||
|
.Ldstr(_className) |
||||
|
.Ldloc(bloc.Local) |
||||
|
// TODO: provide anchor?
|
||||
|
.Ldnull(); |
||||
|
emitter.EmitCall(_types.ClassesBindMethod, true); |
||||
|
} |
||||
|
|
||||
|
public IXamlType TargetType => _types.StyledElement; |
||||
|
|
||||
|
public PropertySetterBinderParameters BinderParameters { get; } = |
||||
|
new PropertySetterBinderParameters { AllowXNull = false }; |
||||
|
public IReadOnlyList<IXamlType> Parameters { get; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using XamlX.Ast; |
||||
|
using XamlX.Transform; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
||||
|
{ |
||||
|
class AvaloniaXamlIlReorderClassesPropertiesTransformer : IXamlAstTransformer |
||||
|
{ |
||||
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) |
||||
|
{ |
||||
|
if (node is XamlAstObjectNode obj) |
||||
|
{ |
||||
|
IXamlAstNode classesNode = null; |
||||
|
IXamlAstNode firstSingleClassNode = null; |
||||
|
var types = context.GetAvaloniaTypes(); |
||||
|
foreach (var child in obj.Children) |
||||
|
{ |
||||
|
if (child is XamlAstXamlPropertyValueNode propValue |
||||
|
&& propValue.Property is XamlAstClrProperty prop) |
||||
|
{ |
||||
|
if (prop.DeclaringType.Equals(types.Classes)) |
||||
|
{ |
||||
|
if (firstSingleClassNode == null) |
||||
|
firstSingleClassNode = child; |
||||
|
} |
||||
|
else if (prop.Name == "Classes" && prop.DeclaringType.Equals(types.StyledElement)) |
||||
|
classesNode = child; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (classesNode != null && firstSingleClassNode != null) |
||||
|
{ |
||||
|
obj.Children.Remove(classesNode); |
||||
|
obj.Children.Insert(obj.Children.IndexOf(firstSingleClassNode), classesNode); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return node; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue