Browse Source

Merge branch 'master' into fixes/HotKey_Command_Issues

pull/4737/head
Steven Kirk 6 years ago
committed by GitHub
parent
commit
dd7c935415
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  2. 11
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  3. 4
      src/Avalonia.Build.Tasks/SpanCompat.cs
  4. 10
      src/Avalonia.Controls/GridLength.cs
  5. 6
      src/Avalonia.Controls/ItemsControl.cs
  6. 4
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  7. 4
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  8. 4
      src/Avalonia.MicroCom/MicroComVtblBase.cs
  9. 2
      src/Avalonia.Native/WindowImplBase.cs
  10. 14
      src/Avalonia.Visuals/Media/Color.cs
  11. 9
      src/Avalonia.Visuals/Media/KnownColors.cs
  12. 2
      src/Avalonia.X11/XI2Manager.cs
  13. 34
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs
  14. 135
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  15. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
  16. 9
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  17. 19
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  18. 34
      src/Windows/Avalonia.Win32/WindowImpl.cs
  19. 5
      src/tools/MicroComGenerator/CppGen.cs
  20. 92
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  21. 112
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  22. 21
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

29
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Dialogs;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
#pragma warning disable 4014
@ -112,11 +113,29 @@ namespace ControlCatalog.Pages
private Window CreateSampleWindow()
{
var window = new Window();
window.Height = 200;
window.Width = 200;
window.Content = new TextBlock { Text = "Hello world!" };
window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
Button button;
var window = new Window
{
Height = 200,
Width = 200,
Content = new StackPanel
{
Spacing = 4,
Children =
{
new TextBlock { Text = "Hello world!" },
(button = new Button
{
HorizontalAlignment = HorizontalAlignment.Center,
Content = "Click to close"
})
}
},
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
button.Click += (_, __) => window.Close();
return window;
}

11
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="$(Configuration) == 'Debug'">netstandard2.0;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks Condition="$(Configuration) == 'Debug'">netstandard2.0;netcoreapp3.1</TargetFrameworks>
<OutputType>exe</OutputType>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
@ -81,6 +81,15 @@
<Compile Include="../Avalonia.Visuals/CornerRadius.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/Color.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/KnownColors.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Controls/GridLength.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<PackageReference Include="Mono.Cecil" Version="0.11.2" />

4
src/Avalonia.Build.Tasks/SpanCompat.cs

@ -1,3 +1,4 @@
#if !NETCOREAPP3_1
namespace System
{
// This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory
@ -63,6 +64,8 @@ namespace System
}
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
public static implicit operator ReadOnlySpan<T>(char[] arr) => new ReadOnlySpan<T>(new string(arr));
}
static class SpanCompatExtensions
@ -71,3 +74,4 @@ namespace System
}
}
#endif

10
src/Avalonia.Controls/GridLength.cs

@ -8,7 +8,10 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the valid units for a <see cref="GridLength"/>.
/// </summary>
public enum GridUnitType
#if !BUILDTASK
public
#endif
enum GridUnitType
{
/// <summary>
/// The row or column is auto-sized to fit its content.
@ -29,7 +32,10 @@ namespace Avalonia.Controls
/// <summary>
/// Holds the width or height of a <see cref="Grid"/>'s column and row definitions.
/// </summary>
public struct GridLength : IEquatable<GridLength>
#if !BUILDTASK
public
#endif
struct GridLength : IEquatable<GridLength>
{
private readonly GridUnitType _type;

6
src/Avalonia.Controls/ItemsControl.cs

@ -449,11 +449,7 @@ namespace Avalonia.Controls
if (_itemContainerGenerator != null)
{
_itemContainerGenerator.ItemTemplate = (IDataTemplate)e.NewValue;
if (e.OldValue != null && Presenter != null)
{
Presenter.ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
// TODO: Rebuild the item containers.
}
}

4
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -200,10 +200,6 @@ namespace Avalonia.Controls.Presenters
break;
case NotifyCollectionChangedAction.Reset:
Owner.ItemContainerGenerator.Clear();
VirtualizingPanel.Children.Clear();
FirstIndex = NextIndex = 0;
RecycleContainersOnRemove();
CreateAndRemoveContainers();
panel.ForceInvalidateMeasure();

4
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@ -57,8 +57,6 @@ namespace Avalonia.Controls.Presenters
set
{
var itemsReplaced = (_items != value);
_itemsSubscription?.Dispose();
_itemsSubscription = null;
@ -69,7 +67,7 @@ namespace Avalonia.Controls.Presenters
SetAndRaise(ItemsProperty, ref _items, value);
if (_createdPanel && itemsReplaced)
if (_createdPanel)
{
ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}

4
src/Avalonia.MicroCom/MicroComVtblBase.cs

@ -7,10 +7,10 @@ namespace Avalonia.MicroCom
public unsafe class MicroComVtblBase
{
private List<IntPtr> _methods = new List<IntPtr>();
[UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)]
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
private delegate int AddRefDelegate(Ccw* ccw);
[UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)]
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv);
public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable();

2
src/Avalonia.Native/WindowImplBase.cs

@ -174,7 +174,7 @@ namespace Avalonia.Native
void IAvnWindowBaseEvents.Resized(AvnSize* size)
{
if (_parent._native != null)
if (_parent?._native != null)
{
var s = new Size(size->Width, size->Height);
_parent._savedLogicalSize = s;

14
src/Avalonia.Visuals/Media/Color.cs

@ -1,17 +1,24 @@
using System;
using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators;
#endif
namespace Avalonia.Media
{
/// <summary>
/// An ARGB color.
/// </summary>
public readonly struct Color : IEquatable<Color>
#if !BUILDTASK
public
#endif
readonly struct Color : IEquatable<Color>
{
static Color()
{
#if !BUILDTASK
Animation.Animation.RegisterAnimator<ColorAnimator>(prop => typeof(Color).IsAssignableFrom(prop.PropertyType));
#endif
}
/// <summary>
@ -223,7 +230,12 @@ namespace Avalonia.Media
if (input.Length == 3 || input.Length == 4)
{
var extendedLength = 2 * input.Length;
#if !BUILDTASK
Span<char> extended = stackalloc char[extendedLength];
#else
char[] extended = new char[extendedLength];
#endif
for (int i = 0; i < input.Length; i++)
{

9
src/Avalonia.Visuals/Media/KnownColors.cs

@ -8,7 +8,9 @@ namespace Avalonia.Media
{
private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames;
private static readonly IReadOnlyDictionary<uint, string> _knownColors;
#if !BUILDTASK
private static readonly Dictionary<KnownColor, ISolidColorBrush> _knownBrushes;
#endif
static KnownColors()
{
@ -32,14 +34,19 @@ namespace Avalonia.Media
_knownColorNames = knownColorNames;
_knownColors = knownColors;
#if !BUILDTASK
_knownBrushes = new Dictionary<KnownColor, ISolidColorBrush>();
#endif
}
#if !BUILDTASK
public static ISolidColorBrush GetKnownBrush(string s)
{
var color = GetKnownColor(s);
return color != KnownColor.None ? color.ToBrush() : null;
}
#endif
public static KnownColor GetKnownColor(string s)
{
@ -61,6 +68,7 @@ namespace Avalonia.Media
return Color.FromUInt32((uint)color);
}
#if !BUILDTASK
public static ISolidColorBrush ToBrush(this KnownColor color)
{
lock (_knownBrushes)
@ -74,6 +82,7 @@ namespace Avalonia.Media
return brush;
}
}
#endif
}
internal enum KnownColor : uint

2
src/Avalonia.X11/XI2Manager.cs

@ -351,7 +351,7 @@ namespace Avalonia.X11
if (state.HasFlag(XModifierMask.Mod4Mask))
Modifiers |= RawInputModifiers.Meta;
Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask);
Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask);
Valuators = new Dictionary<int, double>();
Position = new Point(ev->event_x, ev->event_y);

34
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs

@ -0,0 +1,34 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlGridLengthAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly AvaloniaXamlIlWellKnownTypes _types;
private readonly GridLength _gridLength;
public AvaloniaXamlIlGridLengthAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, GridLength gridLength) : base(lineInfo)
{
_types = types;
_gridLength = gridLength;
Type = new XamlAstClrTypeReference(lineInfo, types.GridLength, false);
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen
.Ldc_R8(_gridLength.Value)
.Ldc_I4((int)_gridLength.GridUnitType)
.Newobj(_types.GridLengthConstructorValueType);
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
}

135
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@ -2,8 +2,10 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using Avalonia.Media;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
@ -205,67 +207,140 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
return true;
}
if (type.Equals(types.Thickness))
{
var thickness = Thickness.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor,
new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });
try
{
var thickness = Thickness.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor,
new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });
return true;
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node);
}
}
if (type.Equals(types.Point))
{
var point = Point.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor,
new[] { point.X, point.Y });
try
{
var point = Point.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor,
new[] { point.X, point.Y });
return true;
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node);
}
}
if (type.Equals(types.Vector))
{
var vector = Vector.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor,
new[] { vector.X, vector.Y });
try
{
var vector = Vector.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor,
new[] { vector.X, vector.Y });
return true;
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node);
}
}
if (type.Equals(types.Size))
{
var size = Size.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor,
new[] { size.Width, size.Height });
try
{
var size = Size.Parse(text);
return true;
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor,
new[] { size.Width, size.Height });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node);
}
}
if (type.Equals(types.Matrix))
{
var matrix = Matrix.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor,
new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 });
try
{
var matrix = Matrix.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor,
new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 });
return true;
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node);
}
}
if (type.Equals(types.CornerRadius))
{
var cornerRadius = CornerRadius.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor,
new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft });
try
{
var cornerRadius = CornerRadius.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor,
new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node);
}
}
if (type.Equals(types.Color))
{
if (!Color.TryParse(text, out Color color))
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.GetMethod(
new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }),
new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) });
return true;
}
if (type.Equals(types.GridLength))
{
try
{
var gridLength = GridLength.Parse(text);
result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
}
}
if (type.FullName == "Avalonia.AvaloniaProperty")
{
var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs

@ -22,7 +22,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var avaloniaObject = context.Configuration.TypeSystem.FindType("Avalonia.AvaloniaObject");
if (avaloniaObject.IsAssignableFrom(targetRef.Type)
&& avaloniaObject.IsAssignableFrom(declaringRef.Type)
&& !targetRef.Type.IsAssignableFrom(declaringRef.Type))
&& !declaringRef.Type.IsAssignableFrom(targetRef.Type))
{
// Instance property
var clrProp = declaringRef.Type.GetAllProperties().FirstOrDefault(p => p.Name == prop.Name);

9
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -49,6 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType ReflectionBindingExtension { get; }
public IXamlType RelativeSource { get; }
public IXamlType UInt { get; }
public IXamlType Long { get; }
public IXamlType Uri { get; }
public IXamlType FontFamily { get; }
@ -65,6 +66,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlConstructor MatrixFullConstructor { get; }
public IXamlType CornerRadius { get; }
public IXamlConstructor CornerRadiusFullConstructor { get; }
public IXamlType GridLength { get; }
public IXamlConstructor GridLengthConstructorValueType { get; }
public IXamlType Color { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -122,6 +126,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater");
ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension");
RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource");
UInt = cfg.TypeSystem.GetType("System.UInt32");
Long = cfg.TypeSystem.GetType("System.Int64");
Uri = cfg.TypeSystem.GetType("System.Uri");
FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily");
@ -141,6 +146,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
(Size, SizeFullConstructor) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2);
(Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6);
(CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4);
GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength");
GridLengthConstructorValueType = GridLength.GetConstructor(new List<IXamlType> { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") });
Color = cfg.TypeSystem.GetType("Avalonia.Media.Color");
}
}

19
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -65,23 +65,10 @@ namespace Avalonia.Win32
return IntPtr.Zero;
}
// Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337
// We need to enable parent window before destroying child window to prevent OS from activating a random window behind us.
// This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks
// Our window closed callback will set enabled state to a correct value after child window gets destroyed.
// We need to verify if parent is still alive (perhaps it got destroyed somehow).
if (_parent != null && IsWindow(_parent._hwnd))
{
var wasActive = GetActiveWindow() == _hwnd;
_parent.SetEnabled(true);
BeforeCloseCleanup(false);
// We also need to activate our parent window since again OS might try to activate a window behind if it is not set.
if (wasActive)
{
SetActiveWindow(_parent._hwnd);
}
}
// Used to distinguish between programmatic and regular close requests.
_isCloseRequested = true;
break;
}

34
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -83,6 +83,7 @@ namespace Avalonia.Win32
private POINT _maxTrackSize;
private WindowImpl _parent;
private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default;
private bool _isCloseRequested;
public WindowImpl()
{
@ -506,6 +507,13 @@ namespace Avalonia.Win32
if (_hwnd != IntPtr.Zero)
{
// Detect if we are being closed programmatically - this would mean that WM_CLOSE was not called
// and we didn't prepare this window for destruction.
if (!_isCloseRequested)
{
BeforeCloseCleanup(true);
}
DestroyWindow(_hwnd);
_hwnd = IntPtr.Zero;
}
@ -948,6 +956,32 @@ namespace Avalonia.Win32
SetFocus(_hwnd);
}
}
private void BeforeCloseCleanup(bool isDisposing)
{
// Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337
// We need to enable parent window before destroying child window to prevent OS from activating a random window behind us (or last active window).
// This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks
// We need to verify if parent is still alive (perhaps it got destroyed somehow).
if (_parent != null && IsWindow(_parent._hwnd))
{
var wasActive = GetActiveWindow() == _hwnd;
// We can only set enabled state if we are not disposing - generally Dispose happens after enabled state has been set.
// Ignoring this would cause us to enable a window that might be disabled.
if (!isDisposing)
{
// Our window closed callback will set enabled state to a correct value after child window gets destroyed.
_parent.SetEnabled(true);
}
// We also need to activate our parent window since again OS might try to activate a window behind if it is not set.
if (wasActive)
{
SetActiveWindow(_parent._hwnd);
}
}
}
private void MaximizeWithoutCoveringTaskbar()
{

5
src/tools/MicroComGenerator/CppGen.cs

@ -14,7 +14,10 @@ namespace MicroComGenerator
name = "unsigned char";
else if(name == "uint")
name = "unsigned int";
return name + new string('*', type.PointerLevel);
type = type.Clone();
type.Name = name;
return type.Format();
}
public static string GenerateCpp(AstIdlNode idl)

92
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@ -596,98 +596,6 @@ namespace Avalonia.Controls.UnitTests
root.Child = target;
}
[Fact]
public void Presenter_Items_Should_Be_In_Sync_When_Replacing_Items()
{
var target = new ItemsControl
{
Template = GetTemplate(),
Items = new[]
{
new Item("Item1")
}
};
var root = new TestRoot { Child = target };
var otherPanel = new StackPanel();
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value);
materializedEventCallCount++;
};
target.Items = new[]
{
new Item("Item2")
};
//Ensure that events are called one time only
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
}
[Fact]
public void Presenter_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate()
{
var target = new ItemsControl
{
Template = GetTemplate(),
Items = new[]
{
new Item("Item1")
},
ItemTemplate = new FuncDataTemplate<Item>((x, ns) => new TextBlock())
};
var root = new TestRoot { Child = target };
var otherPanel = new StackPanel();
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl);
contentPresenter.UpdateChild();
Assert.IsType<TextBlock>(contentPresenter.Child);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl);
contentPresenter.UpdateChild();
Assert.IsType<Canvas>(contentPresenter.Child);
materializedEventCallCount++;
};
target.ItemTemplate =
new FuncDataTemplate<Item>((x, ns) => new Canvas());
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
}
private class Item
{
public Item(string value)

112
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -454,118 +454,6 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void ListBox_Presenter_Items_Should_Be_In_Sync_When_Replacing_Items()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var wnd = new Window() { Width = 100, Height = 100, IsVisible = true };
var target = new ListBox()
{
VerticalAlignment = Layout.VerticalAlignment.Top,
AutoScrollToSelectedItem = true,
Width = 50,
VirtualizationMode = ItemVirtualizationMode.Simple,
Items = new[]
{
new Item("Item1")
},
};
wnd.Content = target;
var lm = wnd.LayoutManager;
lm.ExecuteInitialLayoutPass();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value);
materializedEventCallCount++;
};
target.Items = new[]
{
new Item("Item2")
};
//assert that materialize/dematerialize events are called exactly one time
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
}
}
[Fact]
public void ListBox_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var wnd = new Window() { Width = 100, Height = 100, IsVisible = true };
var target = new ListBox()
{
VerticalAlignment = Layout.VerticalAlignment.Top,
AutoScrollToSelectedItem = true,
Width = 50,
VirtualizationMode = ItemVirtualizationMode.Simple,
Items = new[]
{
new Item("Item1")
},
ItemTemplate =
new FuncDataTemplate<Item>((x, ns) => new Canvas())
};
wnd.Content = target;
var lm = wnd.LayoutManager;
lm.ExecuteInitialLayoutPass();
int dematerializedEventCallCount = 0;
target.ItemContainerGenerator.Dematerialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
Assert.IsType<Canvas>(((ListBoxItem)e.Containers[0].ContainerControl).Presenter.Child);
dematerializedEventCallCount++;
};
int materializedEventCallCount = 0;
ListBoxItem materializedListBoxItem = null;
target.ItemContainerGenerator.Materialized += (s, e) =>
{
Assert.IsType<Item>(e.Containers[0].Item);
Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value);
materializedListBoxItem = ((ListBoxItem)e.Containers[0].ContainerControl);
materializedEventCallCount++;
};
target.ItemTemplate =
new FuncDataTemplate<Item>((x, ns) => new TextBlock());
//ensure events are called only one time
Assert.Equal(1, dematerializedEventCallCount);
Assert.Equal(1, materializedEventCallCount);
wnd.LayoutManager.ExecuteLayoutPass();
//ensure that new template has been applied
Assert.IsType<TextBlock>(materializedListBoxItem.Presenter.Child);
}
}
private FuncControlTemplate ListBoxTemplate()
{
return new FuncControlTemplate<ListBox>((parent, scope) =>

21
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

@ -295,8 +295,27 @@ namespace Avalonia.Markup.Xaml.UnitTests
Assert.Equal("Test", templated.Text);
}
}
[Fact]
public void Should_Work_With_Base_Property()
{
var parsed = (ListBox)AvaloniaRuntimeXamlLoader.Load(@"
<ListBox
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns='https://github.com/avaloniaui'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests;assembly=Avalonia.Markup.Xaml.UnitTests'
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content='{Binding}' />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>");
Assert.NotNull(parsed.ItemTemplate);
}
}
public class XamlIlBugTestsEventHandlerCodeBehind : Window
{
public object SavedContext;

Loading…
Cancel
Save