Browse Source

Merge branch 'master' into fixes/2325-contextmenu

pull/2332/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
bd2cd5a9b6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs
  2. 8
      samples/ControlCatalog/SideBar.xaml
  3. 2
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  4. 6
      src/Avalonia.Controls/Design.cs
  5. 4
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  6. 4
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  7. 25
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  8. 23
      src/Avalonia.Controls/Shapes/Shape.cs
  9. 12
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  10. 12
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  11. 2
      src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
  12. 8
      src/Avalonia.Remote.Protocol/DesignMessages.cs
  13. BIN
      src/Avalonia.Remote.Protocol/Key.snk
  14. 2
      src/Avalonia.Remote.Protocol/MetsysBson.cs
  15. 79
      src/Avalonia.Styling/Styling/Styles.cs
  16. 2
      src/Avalonia.Visuals/Media/DrawingContext.cs
  17. 7
      src/Avalonia.Visuals/Media/GeometryDrawing.cs
  18. 11
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  19. 2
      src/Avalonia.Visuals/Visual.cs
  20. 2
      src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
  21. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  22. 4
      src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
  23. 40
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  24. 99
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaEventConverter.cs
  25. 14
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs
  26. 45
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
  27. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
  28. 4
      src/Markup/Avalonia.Markup/Data/RelativeSource.cs
  29. 16
      tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs
  30. 11
      tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs
  31. 66
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs
  32. 13
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs

2
samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs

@ -21,7 +21,7 @@ namespace BindingDemo.ViewModels
} }
else else
{ {
throw new ArgumentOutOfRangeException("Value must be less than 10."); throw new ArgumentOutOfRangeException(nameof(value), "Value must be less than 10.");
} }
} }
} }

8
samples/ControlCatalog/SideBar.xaml

@ -1,6 +1,14 @@
<Styles xmlns="https://github.com/avaloniaui" <Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.SideBar"> x:Class="ControlCatalog.SideBar">
<Design.PreviewWith>
<Border Padding="20">
<TabControl Classes="sidebar">
<TabItem Header="Item1"/>
<TabItem Header="Item2"/>
</TabControl>
</Border>
</Design.PreviewWith>
<Style Selector="TabControl.sidebar"> <Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/> <Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/> <Setter Property="Padding" Value="8 0 0 0"/>

2
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -21,7 +21,7 @@ namespace Avalonia.Data.Core.Plugins
{ {
if (method.GetParameters().Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8) if (method.GetParameters().Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8)
{ {
var exception = new ArgumentException("Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.", nameof(method)); var exception = new ArgumentException("Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.", nameof(methodName));
return new PropertyError(new BindingNotification(exception, BindingErrorType.Error)); return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
} }

6
src/Avalonia.Controls/Design.cs

@ -48,14 +48,14 @@ namespace Avalonia.Controls
} }
public static readonly AttachedProperty<Control> PreviewWithProperty = AvaloniaProperty public static readonly AttachedProperty<Control> PreviewWithProperty = AvaloniaProperty
.RegisterAttached<Style, Control>("PreviewWith", typeof (Design)); .RegisterAttached<AvaloniaObject, Control>("PreviewWith", typeof (Design));
public static void SetPreviewWith(Style target, Control control) public static void SetPreviewWith(AvaloniaObject target, Control control)
{ {
target.SetValue(PreviewWithProperty, control); target.SetValue(PreviewWithProperty, control);
} }
public static Control GetPreviewWith(Style target) public static Control GetPreviewWith(AvaloniaObject target)
{ {
return target.GetValue(PreviewWithProperty); return target.GetValue(PreviewWithProperty);
} }

4
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -11,11 +11,13 @@ namespace Avalonia.Controls.Embedding.Offscreen
{ {
private double _scaling = 1; private double _scaling = 1;
private Size _clientSize; private Size _clientSize;
public IInputRoot InputRoot { get; private set; } public IInputRoot InputRoot { get; private set; }
public bool IsDisposed { get; private set; }
public virtual void Dispose() public virtual void Dispose()
{ {
//No-op IsDisposed = true;
} }
public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root); public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root);

4
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -965,11 +965,11 @@ namespace Avalonia.Controls
{ {
if (value < Minimum) if (value < Minimum)
{ {
throw new ArgumentOutOfRangeException(nameof(Minimum), string.Format("Value must be greater than Minimum value of {0}", Minimum)); throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be greater than Minimum value of {0}", Minimum));
} }
else if (value > Maximum) else if (value > Maximum)
{ {
throw new ArgumentOutOfRangeException(nameof(Maximum), string.Format("Value must be less than Maximum value of {0}", Maximum)); throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be less than Maximum value of {0}", Maximum));
} }
} }

25
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -61,6 +61,11 @@ namespace Avalonia.Controls.Remote.Server
{ {
var result = InputModifiers.None; var result = InputModifiers.None;
if (modifiers == null)
{
return result;
}
foreach(var modifier in modifiers) foreach(var modifier in modifiers)
{ {
switch (modifier) switch (modifier)
@ -265,11 +270,15 @@ namespace Avalonia.Controls.Remote.Server
var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4; var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4;
var data = new byte[width * height * bpp]; var data = new byte[width * height * bpp];
var handle = GCHandle.Alloc(data, GCHandleType.Pinned); var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try try
{ {
_framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), new PixelSize(width, height), width * bpp, _dpi, (PixelFormat)fmt, if (width > 0 && height > 0)
null); {
Paint?.Invoke(new Rect(0, 0, width, height)); _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), new PixelSize(width, height), width * bpp, _dpi, (PixelFormat)fmt,
null);
Paint?.Invoke(new Rect(0, 0, width, height));
}
} }
finally finally
{ {
@ -301,8 +310,7 @@ namespace Avalonia.Controls.Remote.Server
return; return;
} }
if (ClientSize.Width < 1 || ClientSize.Height < 1)
return;
var format = ProtocolPixelFormat.Rgba8888; var format = ProtocolPixelFormat.Rgba8888;
foreach(var fmt in _supportedFormats) foreach(var fmt in _supportedFormats)
if (fmt <= ProtocolPixelFormat.MaxValue) if (fmt <= ProtocolPixelFormat.MaxValue)
@ -323,8 +331,11 @@ namespace Avalonia.Controls.Remote.Server
public override void Invalidate(Rect rect) public override void Invalidate(Rect rect)
{ {
_invalidated = true; if (!IsDisposed)
Dispatcher.UIThread.Post(RenderIfNeeded); {
_invalidated = true;
Dispatcher.UIThread.Post(RenderIfNeeded);
}
} }
public override IMouseDevice MouseDevice { get; } = new MouseDevice(); public override IMouseDevice MouseDevice { get; } = new MouseDevice();

23
src/Avalonia.Controls/Shapes/Shape.cs

@ -195,7 +195,7 @@ namespace Avalonia.Controls.Shapes
if (deferCalculateTransform) if (deferCalculateTransform)
{ {
_calculateTransformOnArrange = true; _calculateTransformOnArrange = true;
return DefiningGeometry.Bounds.Size; return DefiningGeometry?.Bounds.Size ?? Size.Empty;
} }
else else
{ {
@ -217,17 +217,22 @@ namespace Avalonia.Controls.Shapes
private Size CalculateShapeSizeAndSetTransform(Size availableSize) private Size CalculateShapeSizeAndSetTransform(Size availableSize)
{ {
// This should probably use GetRenderBounds(strokeThickness) but then the calculations if (DefiningGeometry != null)
// will multiply the stroke thickness as well, which isn't correct.
var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch);
if (_transform != transform)
{ {
_transform = transform; // This should probably use GetRenderBounds(strokeThickness) but then the calculations
_renderedGeometry = null; // will multiply the stroke thickness as well, which isn't correct.
var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch);
if (_transform != transform)
{
_transform = transform;
_renderedGeometry = null;
}
return size;
} }
return size; return Size.Empty;
} }
internal static (Size, Matrix) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch) internal static (Size, Matrix) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch)

12
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -35,13 +35,13 @@ namespace Avalonia.DesignerSupport
var localAsm = assemblyPath != null ? Assembly.LoadFile(Path.GetFullPath(assemblyPath)) : null; var localAsm = assemblyPath != null ? Assembly.LoadFile(Path.GetFullPath(assemblyPath)) : null;
var loaded = loader.Load(stream, localAsm, null, baseUri); var loaded = loader.Load(stream, localAsm, null, baseUri);
var styles = loaded as Styles; var style = loaded as IStyle;
if (styles != null) if (style != null)
{ {
var substitute = styles.OfType<Style>().Select(Design.GetPreviewWith).FirstOrDefault(s => s != null); var substitute = Design.GetPreviewWith((AvaloniaObject)style);
if (substitute != null) if (substitute != null)
{ {
substitute.Styles.AddRange(styles); substitute.Styles.Add(style);
control = substitute; control = substitute;
} }
else else
@ -51,8 +51,8 @@ namespace Avalonia.DesignerSupport
{ {
new TextBlock {Text = "Styles can't be previewed without Design.PreviewWith. Add"}, new TextBlock {Text = "Styles can't be previewed without Design.PreviewWith. Add"},
new TextBlock {Text = "<Design.PreviewWith>"}, new TextBlock {Text = "<Design.PreviewWith>"},
new TextBlock {Text = " <Border Padding=20><!-- YOUR CONTROL FOR PREVIEW HERE--></Border>"}, new TextBlock {Text = " <Border Padding=20><!-- YOUR CONTROL FOR PREVIEW HERE --></Border>"},
new TextBlock {Text = "<Design.PreviewWith>"}, new TextBlock {Text = "</Design.PreviewWith>"},
new TextBlock {Text = "before setters in your first Style"} new TextBlock {Text = "before setters in your first Style"}
} }
}; };

12
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -8,6 +8,7 @@ using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Designer; using Avalonia.Remote.Protocol.Designer;
using Avalonia.Remote.Protocol.Viewport; using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Threading; using Avalonia.Threading;
using Portable.Xaml;
namespace Avalonia.DesignerSupport.Remote namespace Avalonia.DesignerSupport.Remote
{ {
@ -204,9 +205,18 @@ namespace Avalonia.DesignerSupport.Remote
} }
catch (Exception e) catch (Exception e)
{ {
var xamlException = e as XamlException;
s_transport.Send(new UpdateXamlResultMessage s_transport.Send(new UpdateXamlResultMessage
{ {
Error = e.ToString() Error = e.ToString(),
Exception = new ExceptionDetails
{
ExceptionType = e.GetType().FullName,
Message = e.Message.ToString(),
LineNumber = xamlException?.LineNumber,
LinePosition = xamlException?.LinePosition,
}
}); });
} }
} }

2
src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj

@ -2,6 +2,8 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<DefineConstants>AVALONIA_REMOTE_PROTOCOL;$(DefineConstants)</DefineConstants> <DefineConstants>AVALONIA_REMOTE_PROTOCOL;$(DefineConstants)</DefineConstants>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Avalonia.Input\Key.cs" /> <Compile Include="..\Avalonia.Input\Key.cs" />

8
src/Avalonia.Remote.Protocol/DesignMessages.cs

@ -15,6 +15,7 @@ namespace Avalonia.Remote.Protocol.Designer
{ {
public string Error { get; set; } public string Error { get; set; }
public string Handle { get; set; } public string Handle { get; set; }
public ExceptionDetails Exception { get; set; }
} }
[AvaloniaRemoteMessageGuid("854887CF-2694-4EB6-B499-7461B6FB96C7")] [AvaloniaRemoteMessageGuid("854887CF-2694-4EB6-B499-7461B6FB96C7")]
@ -23,4 +24,11 @@ namespace Avalonia.Remote.Protocol.Designer
public string SessionId { get; set; } public string SessionId { get; set; }
} }
public class ExceptionDetails
{
public string ExceptionType { get; set; }
public string Message { get; set; }
public int? LineNumber { get; set; }
public int? LinePosition { get; set; }
}
} }

BIN
src/Avalonia.Remote.Protocol/Key.snk

Binary file not shown.

2
src/Avalonia.Remote.Protocol/MetsysBson.cs

@ -749,7 +749,7 @@ namespace Metsys.Bson
if (memberExpression.Expression.NodeType != ExpressionType.Parameter && memberExpression.Expression.NodeType != ExpressionType.Convert) if (memberExpression.Expression.NodeType != ExpressionType.Parameter && memberExpression.Expression.NodeType != ExpressionType.Convert)
{ {
throw new ArgumentException(string.Format("Expression '{0}' must resolve to top-level member.", lambdaExpression), "lambdaExpression"); throw new ArgumentException(string.Format("Expression '{0}' must resolve to top-level member.", lambdaExpression), nameof(lambdaExpression));
} }
return memberExpression.Member.Name; return memberExpression.Member.Name;
default: default:

79
src/Avalonia.Styling/Styling/Styles.cs

@ -2,7 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
@ -12,16 +14,17 @@ namespace Avalonia.Styling
/// <summary> /// <summary>
/// A style that consists of a number of child styles. /// A style that consists of a number of child styles.
/// </summary> /// </summary>
public class Styles : AvaloniaList<IStyle>, IStyle, ISetStyleParent public class Styles : AvaloniaObject, IAvaloniaList<IStyle>, IStyle, ISetStyleParent
{ {
private IResourceNode _parent; private IResourceNode _parent;
private IResourceDictionary _resources; private IResourceDictionary _resources;
private AvaloniaList<IStyle> _styles = new AvaloniaList<IStyle>();
private Dictionary<Type, List<IStyle>> _cache; private Dictionary<Type, List<IStyle>> _cache;
public Styles() public Styles()
{ {
ResetBehavior = ResetBehavior.Remove; _styles.ResetBehavior = ResetBehavior.Remove;
this.ForEachItem( _styles.ForEachItem(
x => x =>
{ {
if (x.ResourceParent == null && x is ISetStyleParent setParent) if (x.ResourceParent == null && x is ISetStyleParent setParent)
@ -57,9 +60,18 @@ namespace Avalonia.Styling
() => { }); () => { });
} }
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add => _styles.CollectionChanged += value;
remove => _styles.CollectionChanged -= value;
}
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged; public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <inheritdoc/>
public int Count => _styles.Count;
/// <inheritdoc/> /// <inheritdoc/>
public bool HasResources => _resources?.Count > 0 || this.Any(x => x.HasResources); public bool HasResources => _resources?.Count > 0 || this.Any(x => x.HasResources);
@ -94,6 +106,19 @@ namespace Avalonia.Styling
/// <inheritdoc/> /// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent; IResourceNode IResourceNode.ResourceParent => _parent;
/// <inheritdoc/>
bool ICollection<IStyle>.IsReadOnly => false;
/// <inheritdoc/>
IStyle IReadOnlyList<IStyle>.this[int index] => _styles[index];
/// <inheritdoc/>
public IStyle this[int index]
{
get => _styles[index];
set => _styles[index] = value;
}
/// <summary> /// <summary>
/// Attaches the style to a control if the style's selector matches. /// Attaches the style to a control if the style's selector matches.
/// </summary> /// </summary>
@ -172,6 +197,54 @@ namespace Avalonia.Styling
return false; return false;
} }
/// <inheritdoc/>
public void AddRange(IEnumerable<IStyle> items) => _styles.AddRange(items);
/// <inheritdoc/>
public void InsertRange(int index, IEnumerable<IStyle> items) => _styles.InsertRange(index, items);
/// <inheritdoc/>
public void Move(int oldIndex, int newIndex) => _styles.Move(oldIndex, newIndex);
/// <inheritdoc/>
public void MoveRange(int oldIndex, int count, int newIndex) => _styles.MoveRange(oldIndex, count, newIndex);
/// <inheritdoc/>
public void RemoveAll(IEnumerable<IStyle> items) => _styles.RemoveAll(items);
/// <inheritdoc/>
public void RemoveRange(int index, int count) => _styles.RemoveRange(index, count);
/// <inheritdoc/>
public int IndexOf(IStyle item) => _styles.IndexOf(item);
/// <inheritdoc/>
public void Insert(int index, IStyle item) => _styles.Insert(index, item);
/// <inheritdoc/>
public void RemoveAt(int index) => _styles.RemoveAt(index);
/// <inheritdoc/>
public void Add(IStyle item) => _styles.Add(item);
/// <inheritdoc/>
public void Clear() => _styles.Clear();
/// <inheritdoc/>
public bool Contains(IStyle item) => _styles.Contains(item);
/// <inheritdoc/>
public void CopyTo(IStyle[] array, int arrayIndex) => _styles.CopyTo(array, arrayIndex);
/// <inheritdoc/>
public bool Remove(IStyle item) => _styles.Remove(item);
/// <inheritdoc/>
public IEnumerator<IStyle> GetEnumerator() => _styles.GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator();
/// <inheritdoc/> /// <inheritdoc/>
void ISetStyleParent.SetParent(IResourceNode parent) void ISetStyleParent.SetParent(IResourceNode parent)
{ {

2
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -109,6 +109,8 @@ namespace Avalonia.Media
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry) public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
{ {
Contract.Requires<ArgumentNullException>(geometry != null);
if (brush != null || PenIsVisible(pen)) if (brush != null || PenIsVisible(pen))
{ {
PlatformImpl.DrawGeometry(brush, pen, geometry.PlatformImpl); PlatformImpl.DrawGeometry(brush, pen, geometry.PlatformImpl);

7
src/Avalonia.Visuals/Media/GeometryDrawing.cs

@ -31,7 +31,10 @@
public override void Draw(DrawingContext context) public override void Draw(DrawingContext context)
{ {
context.DrawGeometry(Brush, Pen, Geometry); if (Geometry != null)
{
context.DrawGeometry(Brush, Pen, Geometry);
}
} }
public override Rect GetBounds() public override Rect GetBounds()
@ -41,4 +44,4 @@
return Geometry?.GetRenderBounds(pen) ?? new Rect(); return Geometry?.GetRenderBounds(pen) ?? new Rect();
} }
} }
} }

11
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs

@ -89,9 +89,14 @@ namespace Avalonia.Rendering.SceneGraph
/// <inheritdoc/> /// <inheritdoc/>
public override bool HitTest(Point p) public override bool HitTest(Point p)
{ {
p *= Transform.Invert(); if (Transform.HasInverse)
return (Brush != null && Geometry.FillContains(p)) || {
(Pen != null && Geometry.StrokeContains(Pen, p)); p *= Transform.Invert();
return (Brush != null && Geometry.FillContains(p)) ||
(Pen != null && Geometry.StrokeContains(Pen, p));
}
return false;
} }
} }
} }

2
src/Avalonia.Visuals/Visual.cs

@ -551,7 +551,7 @@ namespace Avalonia
{ {
if (c == null) if (c == null)
{ {
throw new ArgumentNullException("Cannot add null to VisualChildren."); throw new ArgumentNullException(nameof(c), "Cannot add null to VisualChildren.");
} }
if (c.VisualParent != null) if (c.VisualParent != null)

2
src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs

@ -84,7 +84,7 @@ namespace Avalonia.Gtk3
public RenderOp(GtkWidget widget, ManagedCairoSurface surface, double factor, int width, int height) public RenderOp(GtkWidget widget, ManagedCairoSurface surface, double factor, int width, int height)
{ {
_widget = widget; _widget = widget;
_surface = surface ?? throw new ArgumentNullException(); _surface = surface ?? throw new ArgumentNullException(nameof(surface));
_factor = factor; _factor = factor;
_width = width; _width = width;
_height = height; _height = height;

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="AvaloniaXamlLoader.cs" /> <Compile Include="AvaloniaXamlLoader.cs" />
<Compile Include="Converters\AvaloniaUriTypeConverter.cs" /> <Compile Include="Converters\AvaloniaUriTypeConverter.cs" />
<Compile Include="Converters\AvaloniaEventConverter.cs" />
<Compile Include="Converters\FontFamilyTypeConverter.cs" /> <Compile Include="Converters\FontFamilyTypeConverter.cs" />
<Compile Include="Converters\MemberSelectorTypeConverter.cs" /> <Compile Include="Converters\MemberSelectorTypeConverter.cs" />
<Compile Include="Converters\NullableTypeConverter.cs" /> <Compile Include="Converters\NullableTypeConverter.cs" />

4
src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs

@ -11,6 +11,7 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Markup.Xaml namespace Avalonia.Markup.Xaml
{ {
using System.Reflection;
using Avalonia.Media; using Avalonia.Media;
/// <summary> /// <summary>
@ -41,7 +42,8 @@ namespace Avalonia.Markup.Xaml
{ typeof(WindowIcon), typeof(IconTypeConverter) }, { typeof(WindowIcon), typeof(IconTypeConverter) },
{ typeof(CultureInfo), typeof(CultureInfoConverter) }, { typeof(CultureInfo), typeof(CultureInfoConverter) },
{ typeof(Uri), typeof(AvaloniaUriTypeConverter) }, { typeof(Uri), typeof(AvaloniaUriTypeConverter) },
{ typeof(FontFamily), typeof(FontFamilyTypeConverter) } { typeof(FontFamily), typeof(FontFamilyTypeConverter) },
{ typeof(EventInfo), typeof(AvaloniaEventConverter) },
}; };
internal static Type GetBuiltinTypeConverter(Type type) internal static Type GetBuiltinTypeConverter(Type type)

40
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -1,21 +1,20 @@
// Copyright (c) The Avalonia Project. All rights reserved. // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform;
using Portable.Xaml;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using System.Linq; using Avalonia.Controls;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform;
using Portable.Xaml;
namespace Avalonia.Markup.Xaml namespace Avalonia.Markup.Xaml
{ {
@ -24,29 +23,7 @@ namespace Avalonia.Markup.Xaml
/// </summary> /// </summary>
public class AvaloniaXamlLoader public class AvaloniaXamlLoader
{ {
private readonly AvaloniaXamlSchemaContext _context = GetContext(); public bool IsDesignMode { get; set; }
public bool IsDesignMode
{
get => _context.IsDesignMode;
set => _context.IsDesignMode = value;
}
private static AvaloniaXamlSchemaContext GetContext()
{
var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>();
if (result == null)
{
result = AvaloniaXamlSchemaContext.Create();
AvaloniaLocator.CurrentMutable
.Bind<AvaloniaXamlSchemaContext>()
.ToConstant(result);
}
return result;
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class. /// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
@ -188,7 +165,8 @@ namespace Avalonia.Markup.Xaml
LocalAssembly = localAssembly LocalAssembly = localAssembly
}; };
var reader = new XamlXmlReader(stream, _context, readerSettings); var context = IsDesignMode ? AvaloniaXamlSchemaContext.DesignInstance : AvaloniaXamlSchemaContext.Instance;
var reader = new XamlXmlReader(stream, context, readerSettings);
object result = LoadFromReader( object result = LoadFromReader(
reader, reader,

99
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaEventConverter.cs

@ -0,0 +1,99 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.PortableXaml;
using Portable.Xaml;
namespace Avalonia.Markup.Xaml.Converters
{
internal class AvaloniaEventConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var text = value as string;
if (text != null)
{
var rootObjectProvider = context.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
var destinationTypeProvider = context.GetService(typeof(IDestinationTypeProvider)) as IDestinationTypeProvider;
if (rootObjectProvider != null && destinationTypeProvider != null)
{
var target = rootObjectProvider.RootObject;
var eventType = destinationTypeProvider.GetDestinationType();
var eventParameters = eventType.GetRuntimeMethods().First(r => r.Name == "Invoke").GetParameters();
// go in reverse to match System.Xaml behaviour
var methods = target.GetType().GetRuntimeMethods().Reverse();
// find based on exact match parameter types first
foreach (var method in methods)
{
if (method.Name != text)
continue;
var parameters = method.GetParameters();
if (eventParameters.Length != parameters.Length)
continue;
if (parameters.Length == 0)
return method.CreateDelegate(eventType, target);
for (int i = 0; i < parameters.Length; i++)
{
var param = parameters[i];
var eventParam = eventParameters[i];
if (param.ParameterType != eventParam.ParameterType)
break;
if (i == parameters.Length - 1)
return method.CreateDelegate(eventType, target);
}
}
// EnhancedXaml: Find method with compatible base class parameters
foreach (var method in methods)
{
if (method.Name != text)
continue;
var parameters = method.GetParameters();
if (parameters.Length == 0 || eventParameters.Length != parameters.Length)
continue;
for (int i = 0; i < parameters.Length; i++)
{
var param = parameters[i];
var eventParam = eventParameters[i];
if (!param.ParameterType.GetTypeInfo().IsAssignableFrom(eventParam.ParameterType.GetTypeInfo()))
break;
if (i == parameters.Length - 1)
return method.CreateDelegate(eventType, target);
}
}
var contextProvider = (IXamlSchemaContextProvider)context.GetService(typeof(IXamlSchemaContextProvider));
var avaloniaContext = (AvaloniaXamlSchemaContext)contextProvider.SchemaContext;
if (avaloniaContext.IsDesignMode)
{
// We want to ignore missing events in the designer, so if event handler
// wasn't found create an empty delegate.
var lambdaExpression = Expression.Lambda(
eventType,
Expression.Empty(),
eventParameters.Select(x => Expression.Parameter(x.ParameterType)));
return lambdaExpression.Compile();
}
else
{
throw new XamlObjectWriterException($"Referenced value method {text} in type {target.GetType()} indicated by event {eventType.FullName} was not found");
}
}
}
return base.ConvertFrom(context, culture, value);
}
}
}

14
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs

@ -49,6 +49,18 @@ namespace Avalonia.Markup.Xaml.PortableXaml
//Portable.Xaml is not searching for Type Converter //Portable.Xaml is not searching for Type Converter
result = new TypeConverterAttribute(typeof(SetterValueTypeConverter)); result = new TypeConverterAttribute(typeof(SetterValueTypeConverter));
} }
else if (attributeType == typeof(TypeConverterAttribute) && _info is EventInfo)
{
// If a type converter for `EventInfo` is registered, then use that to convert
// event handler values. This is used by the designer to override the lookup
// for event handlers with a null handler.
var eventConverter = AvaloniaTypeConverters.GetTypeConverter(typeof(EventInfo));
if (eventConverter != null)
{
result = new TypeConverterAttribute(eventConverter);
}
}
if (result == null) if (result == null)
{ {
@ -68,4 +80,4 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private readonly MemberInfo _info; private readonly MemberInfo _info;
} }
} }

45
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs

@ -1,21 +1,48 @@
using Avalonia.Data; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Data;
using Avalonia.Markup.Xaml.Context; using Avalonia.Markup.Xaml.Context;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Markup.Xaml.Styling; using Avalonia.Markup.Xaml.Styling;
using Portable.Xaml; using Portable.Xaml;
using Portable.Xaml.ComponentModel;
using System.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Avalonia.Markup.Xaml.PortableXaml namespace Avalonia.Markup.Xaml.PortableXaml
{ {
internal class AvaloniaXamlSchemaContext : XamlSchemaContext internal class AvaloniaXamlSchemaContext : XamlSchemaContext
{ {
public bool IsDesignMode { get; set; } private static AvaloniaXamlSchemaContext s_instance;
private static AvaloniaXamlSchemaContext s_designInstance;
public static AvaloniaXamlSchemaContext Instance
{
get
{
if (s_instance == null)
{
s_instance = Create();
}
return s_instance;
}
}
public static AvaloniaXamlSchemaContext DesignInstance
{
get
{
if (s_designInstance == null)
{
s_designInstance = Create();
s_designInstance.IsDesignMode = true;
}
return s_designInstance;
}
}
public bool IsDesignMode { get; private set; }
public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null) public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null)
{ {
return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider()); return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider());

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs

@ -385,4 +385,4 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{ {
} }
} }
} }

4
src/Markup/Avalonia.Markup/Data/RelativeSource.cs

@ -85,9 +85,9 @@ namespace Avalonia.Data
get { return _ancestorLevel; } get { return _ancestorLevel; }
set set
{ {
if (_ancestorLevel <= 0) if (value <= 0)
{ {
throw new ArgumentOutOfRangeException("AncestorLevel may not be set to less than 1."); throw new ArgumentOutOfRangeException(nameof(value), "AncestorLevel may not be set to less than 1.");
} }
_ancestorLevel = value; _ancestorLevel = value;

16
tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs

@ -0,0 +1,16 @@
using Avalonia.Controls.Shapes;
using Xunit;
namespace Avalonia.Controls.UnitTests.Shapes
{
public class PathTests
{
[Fact]
public void Path_With_Null_Data_Does_Not_Throw_On_Measure()
{
var target = new Path();
target.Measure(Size.Infinity);
}
}
}

11
tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs

@ -9,6 +9,7 @@ using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Designer;
using Avalonia.Remote.Protocol.Viewport; using Avalonia.Remote.Protocol.Viewport;
using Xunit; using Xunit;
@ -109,6 +110,16 @@ namespace Avalonia.DesignerSupport.Tests
return Guid.NewGuid().ToString(); return Guid.NewGuid().ToString();
if (t == typeof(Guid)) if (t == typeof(Guid))
return Guid.NewGuid(); return Guid.NewGuid();
if (t == typeof(Exception))
return new Exception("Here");
if (t == typeof(ExceptionDetails))
return new ExceptionDetails
{
ExceptionType = "Exception",
LineNumber = 5,
LinePosition = 6,
Message = "Here",
};
throw new Exception($"Doesn't know how to fabricate a random value for {t}, path {pathInfo}"); throw new Exception($"Doesn't know how to fabricate a random value for {t}, path {pathInfo}");
} }

66
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs

@ -0,0 +1,66 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Controls;
using Avalonia.Input;
using Portable.Xaml;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
public class EventTests
{
[Fact]
public void Event_Is_Attached()
{
var xaml = @"<Button xmlns='https://github.com/avaloniaui' Click='OnClick'/>";
var loader = new AvaloniaXamlLoader();
var target = new MyButton();
loader.Load(xaml, rootInstance: target);
RaiseClick(target);
Assert.True(target.Clicked);
}
[Fact]
public void Exception_Is_Thrown_If_Event_Not_Found()
{
var xaml = @"<Button xmlns='https://github.com/avaloniaui' Click='NotFound'/>";
var loader = new AvaloniaXamlLoader();
var target = new MyButton();
Assert.Throws<XamlObjectWriterException>(() => loader.Load(xaml, rootInstance: target));
}
[Fact]
public void Exception_Is_Not_Thrown_If_Event_Not_Found_In_Design_Mode()
{
var xaml = @"<Button xmlns='https://github.com/avaloniaui' Click='NotFound'/>";
var loader = new AvaloniaXamlLoader { IsDesignMode = true };
var target = new MyButton();
loader.Load(xaml, rootInstance: target);
}
private void RaiseClick(MyButton target)
{
target.RaiseEvent(new KeyEventArgs
{
RoutedEvent = Button.KeyDownEvent,
Key = Key.Enter,
});
}
class MyButton : Button
{
public bool Clicked { get; private set; }
public void OnClick(object sender, EventArgs e)
{
Clicked = true;
}
}
}
}

13
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs

@ -61,6 +61,19 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(1, bitmap.RefCount); Assert.Equal(1, bitmap.RefCount);
} }
[Fact]
public void HitTest_On_Geometry_Node_With_Zero_Transform_Does_Not_Throw()
{
var geometry = Mock.Of<IGeometryImpl>();
var geometryNode = new GeometryNode(
new Matrix(),
Brushes.Black,
null,
geometry);
geometryNode.HitTest(new Point());
}
private class TestDrawOperation : DrawOperation private class TestDrawOperation : DrawOperation
{ {
public TestDrawOperation(Rect bounds, Matrix transform, Pen pen) public TestDrawOperation(Rect bounds, Matrix transform, Pen pen)

Loading…
Cancel
Save