Răsfoiți Sursa

Merge branch 'master' into skia-gl-flip-y

Tako 3 ani în urmă
părinte
comite
c17ebff30b
21 a modificat fișierele cu 357 adăugiri și 155 ștergeri
  1. 1 1
      .github/PULL_REQUEST_TEMPLATE.md
  2. 1 1
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  3. 1 0
      samples/IntegrationTestApp/MainWindow.axaml.cs
  4. 2 1
      samples/IntegrationTestApp/ShowWindowTest.axaml
  5. 17 8
      src/Android/Avalonia.Android/AndroidPlatform.cs
  6. 20 1
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  7. 6 3
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  8. 7 0
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  9. 8 2
      src/Avalonia.Controls/TransitioningContentControl.cs
  10. 7 0
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  11. 15 0
      src/Avalonia.ReactiveUI/RoutedViewHost.cs
  12. 0 80
      src/Avalonia.ReactiveUI/TransitioningContentControl.cs
  13. 18 0
      src/Avalonia.ReactiveUI/ViewModelViewHost.cs
  14. 43 7
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
  15. 20 6
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
  16. 118 34
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
  17. 1 1
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  18. 2 1
      src/Windows/Avalonia.Win32/WindowImpl.cs
  19. 41 1
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  20. 29 0
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
  21. 0 8
      tests/Avalonia.ReactiveUI.UnitTests/TransitioningContentControlTest.cs

+ 1 - 1
.github/PULL_REQUEST_TEMPLATE.md

@@ -21,7 +21,7 @@
 - [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Documentation with user documentation
 
 ## Breaking changes
-<!--- List any breaking changes here. When the PR is merged please add an entry to https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes -->
+<!--- List any breaking changes here. -->
 
 ## Obsoletions / Deprecations
 <!--- Obsolete and Deprecated attributes on APIs MUST only be included when discussed with Core team. @grokys, @kekekeks & @danwalmsley -->

+ 1 - 1
native/Avalonia.Native/src/OSX/WindowImpl.mm

@@ -121,7 +121,7 @@ void WindowImpl::BringToFront()
 {
     if(Window != nullptr)
     {
-        if (![Window isMiniaturized])
+        if ([Window isVisible] && ![Window isMiniaturized])
         {
             if(IsDialog())
             {

+ 1 - 0
samples/IntegrationTestApp/MainWindow.axaml.cs

@@ -99,6 +99,7 @@ namespace IntegrationTestApp
 
             foreach (var window in lifetime.Windows)
             {
+                window.Show();
                 if (window.WindowState == WindowState.Minimized)
                     window.WindowState = WindowState.Normal;
             }

+ 2 - 1
samples/IntegrationTestApp/ShowWindowTest.axaml

@@ -3,7 +3,7 @@
         x:Class="IntegrationTestApp.ShowWindowTest"
         Name="SecondaryWindow"
         Title="Show Window Test">
-  <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
+  <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
     <Label Grid.Column="0" Grid.Row="1">Client Size</Label>
     <TextBox Name="ClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"
              Text="{Binding ClientSize, Mode=OneWay}"/>
@@ -31,5 +31,6 @@
       <ComboBoxItem>Maximized</ComboBoxItem>
       <ComboBoxItem>Fullscreen</ComboBoxItem>
     </ComboBox>
+    <Button Name="HideButton" Grid.Row="8" Command="{Binding $parent[Window].Hide}">Hide</Button>
   </Grid>
 </Window>

+ 17 - 8
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -8,7 +8,8 @@ using Avalonia.Input.Platform;
 using Avalonia.OpenGL.Egl;
 using Avalonia.Platform;
 using Avalonia.Rendering;
-using Avalonia.Skia;
+using Avalonia.Rendering.Composition;
+using Avalonia.OpenGL;
 
 namespace Avalonia
 {
@@ -16,10 +17,8 @@ namespace Avalonia
     {
         public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
         {
-            var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
-
             return builder
-                .UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android")
+                .UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android")
                 .UseSkia();
         }
     }
@@ -42,9 +41,11 @@ namespace Avalonia.Android
 
         public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500);
 
-        public static void Initialize(AndroidPlatformOptions options)
+        internal static Compositor Compositor { get; private set; }
+
+        public static void Initialize()
         {
-            Options = options;
+            Options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
 
             AvaloniaLocator.CurrentMutable
                 .Bind<IClipboard>().ToTransient<ClipboardImpl>()
@@ -58,16 +59,24 @@ namespace Avalonia.Android
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop())
                 .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
 
-            if (options.UseGpu)
+            if (Options.UseGpu)
             {
                 EglPlatformOpenGlInterface.TryInitialize();
             }
+            
+            if (Options.UseCompositor)
+            {
+                Compositor = new Compositor(
+                    AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(),
+                    AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>());
+            }
         }
     }
 
     public sealed class AndroidPlatformOptions
     {
-        public bool UseDeferredRendering { get; set; } = true;
+        public bool UseDeferredRendering { get; set; } = false;
         public bool UseGpu { get; set; } = true;
+        public bool UseCompositor { get; set; } = true;
     }
 }

+ 20 - 1
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@@ -14,6 +14,7 @@ namespace Avalonia.Android
     internal sealed class AndroidThreadingInterface : IPlatformThreadingInterface
     {
         private Handler _handler;
+        private static Thread s_uiThread;
 
         public AndroidThreadingInterface()
         {
@@ -76,7 +77,25 @@ namespace Avalonia.Android
             EnsureInvokeOnMainThread(() => Signaled?.Invoke(null));
         }
 
-        public bool CurrentThreadIsLoopThread => Looper.MainLooper.Thread.Equals(Java.Lang.Thread.CurrentThread());
+        public bool CurrentThreadIsLoopThread
+        {
+            get
+            {
+                if (s_uiThread != null)
+                    return s_uiThread == Thread.CurrentThread;
+
+                var isOnMainThread = OperatingSystem.IsAndroidVersionAtLeast(23)
+                    ? Looper.MainLooper.IsCurrentThread
+                    : Looper.MainLooper.Thread.Equals(Java.Lang.Thread.CurrentThread());
+                if (isOnMainThread)
+                {
+                    s_uiThread = Thread.CurrentThread;
+                    return true;
+                }
+
+                return false;
+            }
+        }
         public event Action<DispatcherPriority?> Signaled;
     }
 }

+ 6 - 3
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -19,6 +19,7 @@ using Avalonia.OpenGL.Surfaces;
 using Avalonia.Platform;
 using Avalonia.Platform.Storage;
 using Avalonia.Rendering;
+using Avalonia.Rendering.Composition;
 
 namespace Avalonia.Android.Platform.SkiaPlatform
 {
@@ -84,9 +85,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         public IEnumerable<object> Surfaces => new object[] { _gl, _framebuffer, Handle };
 
         public IRenderer CreateRenderer(IRenderRoot root) =>
-            AndroidPlatform.Options.UseDeferredRendering
-            ? new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>()) { RenderOnlyOnRenderThread = true }
-            : new ImmediateRenderer(root);
+            AndroidPlatform.Options.UseCompositor
+                ? new CompositingRenderer(root, AndroidPlatform.Compositor)
+                : AndroidPlatform.Options.UseDeferredRendering
+                    ? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService<IRenderLoop>()) { RenderOnlyOnRenderThread = true }
+                    : new ImmediateRenderer(root);
 
         public virtual void Hide()
         {

+ 7 - 0
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@@ -78,6 +78,13 @@ namespace Avalonia.Rendering.Composition.Server
 
             if (Root == null) 
                 return;
+
+            if ((_renderTarget as IRenderTargetWithCorruptionInfo)?.IsCorrupted == true)
+            {
+                _renderTarget!.Dispose();
+                _renderTarget = null;
+            }
+
             _renderTarget ??= _renderTargetFactory();
 
             Compositor.UpdateServerTime();

+ 8 - 2
src/Avalonia.Controls/TransitioningContentControl.cs

@@ -84,13 +84,19 @@ public class TransitioningContentControl : ContentControl
 
         _lastTransitionCts?.Cancel();
         _lastTransitionCts = new CancellationTokenSource();
+        var localToken = _lastTransitionCts.Token;
 
         if (PageTransition != null)
-            await PageTransition.Start(this, null, true, _lastTransitionCts.Token);
+            await PageTransition.Start(this, null, true, localToken);
+
+        if (localToken.IsCancellationRequested)
+        {
+            return;
+        }
 
         CurrentContent = content;
 
         if (PageTransition != null)
-            await PageTransition.Start(null, this, true, _lastTransitionCts.Token);
+            await PageTransition.Start(null, this, true, localToken);
     }
 }

+ 7 - 0
src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@@ -146,6 +146,13 @@ namespace Avalonia.OpenGL.Controls
                 return false;
             }
 
+            if (_context == null)
+            {
+                Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
+                    "Unable to initialize OpenGL: unable to create additional OpenGL context.");
+                return false;
+            }
+
             GlVersion = _context.Version;
             try
             {

+ 15 - 0
src/Avalonia.ReactiveUI/RoutedViewHost.cs

@@ -64,6 +64,12 @@ namespace Avalonia.ReactiveUI
         public static readonly StyledProperty<string?> ViewContractProperty =
             AvaloniaProperty.Register<RoutedViewHost, string?>(nameof(ViewContract));
 
+        /// <summary>
+        /// <see cref="AvaloniaProperty"/> for the <see cref="DefaultContent"/> property.
+        /// </summary>
+        public static readonly StyledProperty<object?> DefaultContentProperty =
+            ViewModelViewHost.DefaultContentProperty.AddOwner<RoutedViewHost>();
+
         /// <summary>
         /// Initializes a new instance of the <see cref="RoutedViewHost"/> class.
         /// </summary>
@@ -106,6 +112,15 @@ namespace Avalonia.ReactiveUI
             set => SetValue(ViewContractProperty, value);
         }
 
+        /// <summary>
+        /// Gets or sets the content displayed whenever there is no page currently routed.
+        /// </summary>
+        public object? DefaultContent
+        {
+            get => GetValue(DefaultContentProperty);
+            set => SetValue(DefaultContentProperty, value);
+        }
+
         /// <summary>
         /// Gets or sets the ReactiveUI view locator used by this router.
         /// </summary>

+ 0 - 80
src/Avalonia.ReactiveUI/TransitioningContentControl.cs

@@ -1,80 +0,0 @@
-using System;
-using System.Threading;
-
-using Avalonia.Animation;
-using Avalonia.Controls;
-using Avalonia.Styling;
-
-namespace Avalonia.ReactiveUI
-{
-    /// <summary>
-    /// A ContentControl that animates the transition when its content is changed.
-    /// </summary>
-    [Obsolete("Use TransitioningContentControl in Avalonia.Controls namespace")]
-    public class TransitioningContentControl : ContentControl, IStyleable
-    {
-        /// <summary>
-        /// <see cref="AvaloniaProperty"/> for the <see cref="PageTransition"/> property.
-        /// </summary>
-        public static readonly StyledProperty<IPageTransition?> PageTransitionProperty =
-            AvaloniaProperty.Register<TransitioningContentControl, IPageTransition?>(nameof(PageTransition),
-                new CrossFade(TimeSpan.FromSeconds(0.5)));
-
-        /// <summary>
-        /// <see cref="AvaloniaProperty"/> for the <see cref="DefaultContent"/> property.
-        /// </summary>
-        public static readonly StyledProperty<object?> DefaultContentProperty =
-            AvaloniaProperty.Register<TransitioningContentControl, object?>(nameof(DefaultContent));
-
-        private CancellationTokenSource? _lastTransitionCts;
-
-        /// <summary>
-        /// Gets or sets the animation played when content appears and disappears.
-        /// </summary>
-        public IPageTransition? PageTransition
-        {
-            get => GetValue(PageTransitionProperty);
-            set => SetValue(PageTransitionProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the content displayed whenever there is no page currently routed.
-        /// </summary>
-        public object? DefaultContent
-        {
-            get => GetValue(DefaultContentProperty);
-            set => SetValue(DefaultContentProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the content with animation.
-        /// </summary>
-        public new object? Content
-        {
-            get => base.Content;
-            set => UpdateContentWithTransition(value);
-        }
-        
-        /// <summary>
-        /// TransitioningContentControl uses the default ContentControl 
-        /// template from Avalonia default theme.
-        /// </summary>
-        Type IStyleable.StyleKey => typeof(ContentControl);
-
-        /// <summary>
-        /// Updates the content with transitions.
-        /// </summary>
-        /// <param name="content">New content to set.</param>
-        private async void UpdateContentWithTransition(object? content)
-        {
-            _lastTransitionCts?.Cancel();
-            _lastTransitionCts = new CancellationTokenSource();
-
-            if (PageTransition != null)
-                await PageTransition.Start(this, null, true, _lastTransitionCts.Token);
-            base.Content = content;
-            if (PageTransition != null)
-                await PageTransition.Start(null, this, true, _lastTransitionCts.Token);
-        }
-    }
-}

+ 18 - 0
src/Avalonia.ReactiveUI/ViewModelViewHost.cs

@@ -1,5 +1,8 @@
 using System;
 using System.Reactive.Disposables;
+
+using Avalonia.Controls;
+
 using ReactiveUI;
 using Splat;
 
@@ -24,6 +27,12 @@ namespace Avalonia.ReactiveUI
         public static readonly StyledProperty<string?> ViewContractProperty =
             AvaloniaProperty.Register<ViewModelViewHost, string?>(nameof(ViewContract));
 
+        /// <summary>
+        /// <see cref="AvaloniaProperty"/> for the <see cref="DefaultContent"/> property.
+        /// </summary>
+        public static readonly StyledProperty<object?> DefaultContentProperty =
+            AvaloniaProperty.Register<ViewModelViewHost, object?>(nameof(DefaultContent));
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ViewModelViewHost"/> class.
         /// </summary>
@@ -55,6 +64,15 @@ namespace Avalonia.ReactiveUI
             set => SetValue(ViewContractProperty, value);
         }
 
+        /// <summary>
+        /// Gets or sets the content displayed whenever there is no page currently routed.
+        /// </summary>
+        public object? DefaultContent
+        {
+            get => GetValue(DefaultContentProperty);
+            set => SetValue(DefaultContentProperty, value);
+        }
+
         /// <summary>
         /// Gets or sets the view locator.
         /// </summary>

+ 43 - 7
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using XamlX.Ast;
 using XamlX.Emit;
@@ -47,7 +48,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             return !node.Type.GetClrType().IsValueType;
         }
         
-        class AdderSetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
+        class AdderSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable<AdderSetter>
         {
             private readonly IXamlMethod _getter;
             private readonly IXamlMethod _adder;
@@ -58,16 +59,22 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                 _adder = adder;
                 TargetType = getter.DeclaringType;
                 Parameters = adder.ParametersWithThis().Skip(1).ToList();
+
+                bool allowNull = Parameters.Last().AcceptsNull();
+                BinderParameters = new PropertySetterBinderParameters
+                {
+                    AllowMultiple = true,
+                    AllowXNull = allowNull,
+                    AllowRuntimeNull = allowNull
+                };
             }
 
             public IXamlType TargetType { get; }
 
-            public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters
-            {
-                AllowMultiple = true
-            };
+            public PropertySetterBinderParameters BinderParameters { get; }
 
             public IReadOnlyList<IXamlType> Parameters { get; }
+
             public void Emit(IXamlILEmitter emitter)
             {
                 var locals = new Stack<XamlLocalsPool.PooledLocal>();
@@ -80,11 +87,40 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                 }
 
                 emitter.EmitCall(_getter);
-                while (locals.Count > 0)
+                while (locals.Count>0)
                     using (var loc = locals.Pop())
                         emitter.Ldloc(loc.Local);
                 emitter.EmitCall(_adder, true);
             }
+
+            public void EmitWithArguments(
+                XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> arguments)
+            {
+                emitter.EmitCall(_getter);
+
+                for (var i = 0; i < arguments.Count; ++i)
+                    context.Emit(arguments[i], emitter, Parameters[i]);
+
+                emitter.EmitCall(_adder, true);
+            }
+
+            public bool Equals(AdderSetter other)
+            {
+                if (ReferenceEquals(null, other))
+                    return false;
+                if (ReferenceEquals(this, other))
+                    return true;
+
+                return _getter.Equals(other._getter) && _adder.Equals(other._adder);
+            }
+
+            public override bool Equals(object obj)
+                => Equals(obj as AdderSetter);
+
+            public override int GetHashCode()
+                => (_getter.GetHashCode() * 397) ^ _adder.GetHashCode();
         }
     }
 }

+ 20 - 6
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs

@@ -75,17 +75,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             {
                 Getter = setterType.Methods.First(m => m.Name == "get_Value");
                 var method = setterType.Methods.First(m => m.Name == "set_Value");
-                Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding));
-                Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType));
-                Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType));
+                Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding, false));
+                Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType, false));
+                Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType, targetType.AcceptsNull()));
             }
             
-            class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
+            sealed class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
             {
                 private readonly IXamlMethod _method;
                 private readonly IXamlType _type;
                 public IXamlType TargetType { get; }
-                public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters();
+                public PropertySetterBinderParameters BinderParameters { get; }
                 public IReadOnlyList<IXamlType> Parameters { get; }
                 public void Emit(IXamlILEmitter codegen)
                 {
@@ -94,13 +94,27 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                     codegen.EmitCall(_method, true);
                 }
 
-                public XamlIlDirectCallPropertySetter(IXamlMethod method, IXamlType type)
+                public XamlIlDirectCallPropertySetter(IXamlMethod method, IXamlType type, bool allowNull)
                 {
                     _method = method;
                     _type = type;
                     Parameters = new[] {type};
                     TargetType = method.ThisOrFirstParameter();
+                    BinderParameters = new PropertySetterBinderParameters
+                    {
+                        AllowXNull = allowNull,
+                        AllowRuntimeNull = allowNull
+                    };
                 }
+
+                private bool Equals(XamlIlDirectCallPropertySetter other) 
+                    => Equals(_method, other._method) && Equals(_type, other._type);
+
+                public override bool Equals(object obj) 
+                    => Equals(obj as XamlIlDirectCallPropertySetter);
+
+                public override int GetHashCode() 
+                    => (_method.GetHashCode() * 397) ^ _type.GetHashCode();
             }
         }
     }

+ 118 - 34
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

@@ -206,38 +206,64 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
             Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field));
         }
 
-        abstract class AvaloniaPropertyCustomSetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
+        abstract class AvaloniaPropertyCustomSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable<AvaloniaPropertyCustomSetter>
         {
-            protected AvaloniaXamlIlWellKnownTypes Types;
-            protected IXamlField AvaloniaProperty;
+            protected readonly AvaloniaXamlIlWellKnownTypes Types;
+            protected readonly IXamlField AvaloniaProperty;
 
-            public AvaloniaPropertyCustomSetter(AvaloniaXamlIlWellKnownTypes types,
+            protected AvaloniaPropertyCustomSetter(
+                AvaloniaXamlIlWellKnownTypes types,
                 IXamlType declaringType,
-                IXamlField avaloniaProperty)
+                IXamlField avaloniaProperty,
+                bool allowNull)
             {
                 Types = types;
                 AvaloniaProperty = avaloniaProperty;
                 TargetType = declaringType;
+                BinderParameters = new PropertySetterBinderParameters
+                {
+                    AllowXNull = allowNull,
+                    AllowRuntimeNull = allowNull
+                };
             }
 
             public IXamlType TargetType { get; }
 
-            public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters
-            {
-                AllowXNull = false
-            };
+            public PropertySetterBinderParameters BinderParameters { get; }
 
             public IReadOnlyList<IXamlType> Parameters { get; set; }
-            public abstract void Emit(IXamlILEmitter codegen);
+
+            public abstract void Emit(IXamlILEmitter emitter);
+
+            public abstract void EmitWithArguments(
+                XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> arguments);
+
+            public bool Equals(AvaloniaPropertyCustomSetter other)
+            {
+                if (ReferenceEquals(null, other))
+                    return false;
+                if (ReferenceEquals(this, other))
+                    return true;
+
+                return GetType() == other.GetType() && AvaloniaProperty.Equals(other.AvaloniaProperty);
+            }
+
+            public override bool Equals(object obj)
+                => Equals(obj as AvaloniaPropertyCustomSetter);
+
+            public override int GetHashCode() 
+                => AvaloniaProperty.GetHashCode();
         }
 
         class BindingSetter : AvaloniaPropertyCustomSetter
         {
             public BindingSetter(AvaloniaXamlIlWellKnownTypes types,
                 IXamlType declaringType,
-                IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty)
+                IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty, false)
             {
-                Parameters = new[] {types.IBinding};
+                Parameters = new[] { types.IBinding };
             }
 
             public override void Emit(IXamlILEmitter emitter)
@@ -246,10 +272,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                     emitter
                         .Stloc(bloc.Local)
                         .Ldsfld(AvaloniaProperty)
-                        .Ldloc(bloc.Local)
-                        // TODO: provide anchor?
-                        .Ldnull();
-                emitter.EmitCall(Types.AvaloniaObjectBindMethod, true);
+                        .Ldloc(bloc.Local);
+                EmitAnchorAndBind(emitter);
+            }
+
+            public override void EmitWithArguments(
+                XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> arguments)
+            {
+                emitter.Ldsfld(AvaloniaProperty);
+                context.Emit(arguments[0], emitter, Parameters[0]);
+                EmitAnchorAndBind(emitter);
+            }
+
+            private void EmitAnchorAndBind(IXamlILEmitter emitter)
+            {
+                emitter
+                    .Ldnull() // TODO: provide anchor?
+                    .EmitCall(Types.AvaloniaObjectBindMethod, true);
             }
         }
 
@@ -257,7 +298,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
         {
             public BindingWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types,
                 IXamlType declaringType,
-                IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty)
+                IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty, false)
             {
                 Parameters = new[] { types.BindingPriority, types.IBinding };
             }
@@ -265,15 +306,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
             public override void Emit(IXamlILEmitter emitter)
             {
                 using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding))
-                using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int))
                     emitter
                         .Stloc(bloc.Local)
-                        .Stloc(priorityLocal.Local)
+                        .Pop() // ignore priority
                         .Ldsfld(AvaloniaProperty)
-                        .Ldloc(bloc.Local)
-                        // TODO: provide anchor?
-                        .Ldnull();
-                emitter.EmitCall(Types.AvaloniaObjectBindMethod, true);
+                        .Ldloc(bloc.Local);
+                EmitAnchorAndBind(emitter);
+            }
+
+            public override void EmitWithArguments(
+                XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> arguments)
+            {
+                emitter.Ldsfld(AvaloniaProperty);
+                context.Emit(arguments[1], emitter, Parameters[1]);
+                EmitAnchorAndBind(emitter);
+            }
+
+            private void EmitAnchorAndBind(IXamlILEmitter emitter)
+            {
+                emitter
+                    .Ldnull() // TODO: provide anchor?
+                    .EmitCall(Types.AvaloniaObjectBindMethod, true);
             }
         }
 
@@ -281,7 +336,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
         {
             public SetValueWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty,
                 IXamlType propertyType)
-                : base(types, declaringType, avaloniaProperty)
+                : base(types, declaringType, avaloniaProperty, propertyType.AcceptsNull())
             {
                 Parameters = new[] { types.BindingPriority, propertyType };
             }
@@ -295,9 +350,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                    - value
                 */
 
-                var method = Types.AvaloniaObjectSetStyledPropertyValue
-                    .MakeGenericMethod(new[] { Parameters[1] });
-
                 using (var valueLocal = emitter.LocalsPool.GetLocal(Parameters[1]))
                 using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int))
                     emitter
@@ -305,25 +357,57 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                         .Stloc(priorityLocal.Local)
                         .Ldsfld(AvaloniaProperty)
                         .Ldloc(valueLocal.Local)
-                        .Ldloc(priorityLocal.Local)
-                        .EmitCall(method, true);
+                        .Ldloc(priorityLocal.Local);
+
+                EmitSetStyledPropertyValue(emitter);
+            }
+
+            public override void EmitWithArguments(
+                XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> arguments)
+            {
+                emitter.Ldsfld(AvaloniaProperty);
+                context.Emit(arguments[1], emitter, Parameters[1]);
+                context.Emit(arguments[0], emitter, Parameters[0]);
+                EmitSetStyledPropertyValue(emitter);
+            }
+
+            private void EmitSetStyledPropertyValue(IXamlILEmitter emitter)
+            {
+                var method = Types.AvaloniaObjectSetStyledPropertyValue.MakeGenericMethod(new[] { Parameters[1] });
+                emitter.EmitCall(method, true);
             }
         }
 
         class UnsetValueSetter : AvaloniaPropertyCustomSetter
         {
             public UnsetValueSetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty) 
-                : base(types, declaringType, avaloniaProperty)
+                : base(types, declaringType, avaloniaProperty, false)
             {
-                Parameters = new[] {types.UnsetValueType};
+                Parameters = new[] { types.UnsetValueType };
             }
 
             public override void Emit(IXamlILEmitter codegen)
             {
+                codegen.Pop();
+                EmitSetValue(codegen);
+            }
+
+            public override void EmitWithArguments(
+                XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> arguments)
+            {
+                EmitSetValue(emitter);
+            }
+
+            private void EmitSetValue(IXamlILEmitter emitter)
+            {
+                // Ignore the instance and load one from the static field to avoid extra local variable
                 var unsetValue = Types.AvaloniaProperty.Fields.First(f => f.Name == "UnsetValue");
-                codegen
-                    // Ignore the instance and load one from the static field to avoid extra local variable
-                    .Pop()
+
+                emitter
                     .Ldsfld(AvaloniaProperty)
                     .Ldsfld(unsetValue)
                     .Ldc_I4(0)

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@@ -1 +1 @@
-Subproject commit a4e6be2d1407abec4f35fcb208848830ce513ead
+Subproject commit c1c0594ec2c35b08988183b1a5b3e34dfa19179d

+ 2 - 1
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -285,11 +285,12 @@ namespace Avalonia.Win32
 
             set
             {
-                if (IsWindowVisible(_hwnd))
+                if (IsWindowVisible(_hwnd) && _lastWindowState != value)
                 {
                     ShowWindow(value, value != WindowState.Minimized); // If the window is minimized, it shouldn't be activated
                 }
 
+                _lastWindowState = value;
                 _showWindowState = value;
             }
         }

+ 41 - 1
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@@ -1,7 +1,9 @@
 using System;
+using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading;
 using Avalonia.Controls;
+using OpenQA.Selenium;
 using OpenQA.Selenium.Appium;
 using OpenQA.Selenium.Interactions;
 using Xunit;
@@ -55,6 +57,43 @@ namespace Avalonia.IntegrationTests.Appium
                 }
             }
         }
+        
+        [PlatformFact(TestPlatforms.Windows)]
+        public void OnWindows_Docked_Windows_Retain_Size_Position_When_Restored()
+        {
+            using (OpenWindow(new Size(400, 400), ShowWindowMode.NonOwned, WindowStartupLocation.Manual))
+            {
+                var windowState = _session.FindElementByAccessibilityId("WindowState");
+
+                Assert.Equal("Normal", windowState.GetComboBoxValue());
+                
+                
+                var window = _session.FindElements(By.XPath("//Window")).First();
+                
+                new Actions(_session)
+                    .KeyDown(Keys.Meta)
+                    .SendKeys(Keys.Left)
+                    .KeyUp(Keys.Meta)
+                    .Perform();
+                
+                var original = GetWindowInfo();
+                
+                windowState.Click();
+                _session.FindElementByName("Minimized").SendClick();
+                
+                new Actions(_session)
+                    .KeyDown(Keys.Alt)
+                    .SendKeys(Keys.Tab)
+                    .KeyUp(Keys.Alt)
+                    .Perform();
+                
+                var current = GetWindowInfo();
+                
+                Assert.Equal(original.Position, current.Position);
+                Assert.Equal(original.FrameSize, current.FrameSize);
+
+            }
+        }
 
 
         [Theory]
@@ -92,7 +131,8 @@ namespace Avalonia.IntegrationTests.Appium
                 Assert.True(clientSize.Width >= current.ScreenRect.Width);
                 Assert.True(clientSize.Height >= current.ScreenRect.Height);
 
-                windowState.Click();
+                windowState.SendClick();
+                
                 _session.FindElementByName("Normal").SendClick();
 
                 current = GetWindowInfo();

+ 29 - 0
tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

@@ -211,6 +211,35 @@ namespace Avalonia.IntegrationTests.Appium
             }
         }
 
+        [PlatformFact(TestPlatforms.MacOS)]
+        public void Hidden_Child_Window_Is_Not_Reshown_When_Parent_Clicked()
+        {
+            var mainWindow = _session.FindElementByAccessibilityId("MainWindow");
+
+            // We don't use dispose to close the window here, because it seems that hiding and re-showing a window
+            // causes Appium to think it's a different window.
+            OpenWindow(null, ShowWindowMode.Owned, WindowStartupLocation.Manual);
+            
+            var secondaryWindow = FindWindow(_session, "SecondaryWindow");
+            var hideButton = secondaryWindow.FindElementByAccessibilityId("HideButton");
+
+            hideButton.Click();
+                
+            var windows = _session.FindElementsByXPath("XCUIElementTypeWindow");
+            Assert.Single(windows);
+                
+            mainWindow.Click();
+                
+            windows = _session.FindElementsByXPath("XCUIElementTypeWindow");
+            Assert.Single(windows);
+                
+            _session.FindElementByAccessibilityId("RestoreAll").Click();
+
+            // Close the window manually.
+            secondaryWindow = FindWindow(_session, "SecondaryWindow");
+            secondaryWindow.GetChromeButtons().close.Click();
+        }
+
         private IDisposable OpenWindow(PixelSize? size, ShowWindowMode mode, WindowStartupLocation location)
         {
             var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize");

+ 0 - 8
tests/Avalonia.ReactiveUI.UnitTests/TransitioningContentControlTest.cs

@@ -12,14 +12,6 @@ namespace Avalonia.ReactiveUI.UnitTests
 {
     public class TransitioningContentControlTest
     {
-        [Fact]
-        public void Transitioning_Control_Should_Derive_Template_From_Content_Control()
-        {
-            var target = new TransitioningContentControl();
-            var stylable = (IStyledElement)target;
-            Assert.Equal(typeof(ContentControl),stylable.StyleKey);
-        }
-
         [Fact]
         public void Transitioning_Control_Template_Should_Be_Instantiated() 
         {