Browse Source

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

pull/8711/head
Tako 4 years ago
committed by GitHub
parent
commit
c17ebff30b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/PULL_REQUEST_TEMPLATE.md
  2. 2
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  3. 1
      samples/IntegrationTestApp/MainWindow.axaml.cs
  4. 3
      samples/IntegrationTestApp/ShowWindowTest.axaml
  5. 25
      src/Android/Avalonia.Android/AndroidPlatform.cs
  6. 21
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  7. 9
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  8. 7
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  9. 10
      src/Avalonia.Controls/TransitioningContentControl.cs
  10. 7
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  11. 15
      src/Avalonia.ReactiveUI/RoutedViewHost.cs
  12. 80
      src/Avalonia.ReactiveUI/TransitioningContentControl.cs
  13. 18
      src/Avalonia.ReactiveUI/ViewModelViewHost.cs
  14. 50
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
  15. 26
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
  16. 152
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
  17. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  18. 3
      src/Windows/Avalonia.Win32/WindowImpl.cs
  19. 42
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  20. 29
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
  21. 8
      tests/Avalonia.ReactiveUI.UnitTests/TransitioningContentControlTest.cs

2
.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 -->

2
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
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;
}

3
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>

25
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;
}
}

21
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;
}
}

9
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
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();

10
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
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
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>

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
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>

50
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();
}
}
}

26
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();
}
}
}

152
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)

2
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

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

3
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;
}
}

42
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
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");

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()
{

Loading…
Cancel
Save