Browse Source

Merge branch 'master' into fixes/static-resource-brush-converter

Dan Walmsley 4 years ago
parent
commit
7bd617229e
26 changed files with 281 additions and 75 deletions
  1. 1 1
      build/SourceLink.props
  2. 6 1
      native/Avalonia.Native/src/OSX/window.mm
  3. 2 0
      packages/Avalonia/AvaloniaBuildTasks.targets
  4. 6 0
      src/Avalonia.Animation/Animation.cs
  5. 6 6
      src/Avalonia.Animation/Animators/Animator`1.cs
  6. 1 2
      src/Avalonia.Animation/ApiCompatBaseline.txt
  7. 3 12
      src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs
  8. 3 4
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  9. 43 9
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  10. 2 0
      src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
  11. 0 3
      src/Avalonia.Controls/Remote/RemoteServer.cs
  12. 1 1
      src/Avalonia.Controls/TextBoxTextInputMethodClient.cs
  13. 1 1
      src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs
  14. 13 12
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
  15. 1 1
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  16. 3 3
      src/Avalonia.FreeDesktop/DBusMenuExporter.cs
  17. 1 1
      src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
  18. 16 7
      src/Avalonia.Themes.Default/Expander.xaml
  19. 22 0
      src/Avalonia.Visuals/Matrix.cs
  20. 5 5
      src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs
  21. 4 1
      src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs
  22. 2 2
      src/Avalonia.X11/X11Window.Xim.cs
  23. 1 0
      src/Avalonia.X11/X11Window.cs
  24. 67 0
      tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
  25. 1 1
      tests/Avalonia.Controls.UnitTests/ItemsSourceViewTests.cs
  26. 70 2
      tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs

+ 1 - 1
build/SourceLink.props

@@ -3,7 +3,7 @@
     <PublishRepositoryUrl>true</PublishRepositoryUrl>
     <IncludeSymbols>false</IncludeSymbols>
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
-    <DebugType>embedded</DebugType>
+    <DebugType>full</DebugType>
     <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
   </PropertyGroup>
   

+ 6 - 1
native/Avalonia.Native/src/OSX/window.mm

@@ -206,7 +206,11 @@ public:
                 auto window = Window;
                 Window = nullptr;
                 
-                [window close];
+                try{
+                    // Seems to throw sometimes on application exit.
+                    [window close];
+                }
+                catch(NSException*){}
             }
             
             return S_OK;
@@ -724,6 +728,7 @@ private:
             if (cparent->WindowState() == Minimized)
                 cparent->SetWindowState(Normal);
             
+            [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
             [cparent->Window addChildWindow:Window ordered:NSWindowAbove];
             
             UpdateStyle();

+ 2 - 0
packages/Avalonia/AvaloniaBuildTasks.targets

@@ -88,6 +88,7 @@
       <AvaloniaXamlReferencesTemporaryFilePath Condition="'$(AvaloniaXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/references</AvaloniaXamlReferencesTemporaryFilePath>
       <AvaloniaXamlOriginalCopyFilePath Condition="'$(AvaloniaXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/original.dll</AvaloniaXamlOriginalCopyFilePath>
       <AvaloniaXamlIlVerifyIl Condition="'$(AvaloniaXamlIlVerifyIl)' == ''">false</AvaloniaXamlIlVerifyIl>
+      <AvaloniaXamlIlDebuggerLaunch Condition="'$(AvaloniaXamlIlDebuggerLaunch)' == ''">false</AvaloniaXamlIlDebuggerLaunch> 
     </PropertyGroup>
     <WriteLinesToFile
       Condition="'$(_AvaloniaForceInternalMSBuild)' != 'true'"
@@ -107,6 +108,7 @@
       DelaySign="$(DelaySign)"
       EnableComInteropPatching="$(_AvaloniaPatchComInterop)"
       SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)"
+      DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)"
     />
     <Exec
       Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"

+ 6 - 0
src/Avalonia.Animation/Animation.cs

@@ -353,6 +353,12 @@ namespace Avalonia.Animation
             return new CompositeDisposable(subscriptions);
         }
 
+        /// <inheritdoc/>
+        public Task RunAsync(Animatable control, IClock clock = null)
+        {
+            return RunAsync(control, clock, default);
+        }
+
         /// <inheritdoc/>
         public Task RunAsync(Animatable control, IClock clock = null, CancellationToken cancellationToken = default)
         {

+ 6 - 6
src/Avalonia.Animation/Animators/Animator`1.cs

@@ -79,15 +79,15 @@ namespace Avalonia.Animation.Animators
 
             T oldValue, newValue;
 
-            if (firstKeyframe.isNeutral)
-                oldValue = neutralValue;
+            if (!firstKeyframe.isNeutral && firstKeyframe.Value is T firstKeyframeValue)
+                oldValue = firstKeyframeValue;
             else
-                oldValue = (T)firstKeyframe.Value;
+                oldValue = neutralValue;
 
-            if (lastKeyframe.isNeutral)
-                newValue = neutralValue;
+            if (!lastKeyframe.isNeutral && lastKeyframe.Value is T lastKeyframeValue)
+                newValue = lastKeyframeValue;
             else
-                newValue = (T)lastKeyframe.Value;
+                newValue = neutralValue;
 
             if (lastKeyframe.KeySpline != null)
                 progress = lastKeyframe.KeySpline.GetSplineProgress(progress);

+ 1 - 2
src/Avalonia.Animation/ApiCompatBaseline.txt

@@ -1,6 +1,5 @@
 Compat issues with assembly Avalonia.Animation:
-MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.Animation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' does not exist in the implementation but it does exist in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' is present in the contract but not in the implementation.
 MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' does not exist in the implementation but it does exist in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock, System.Threading.CancellationToken)' is present in the implementation but not in the contract.
-Total Issues: 4
+Total Issues: 3

+ 3 - 12
src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs

@@ -140,18 +140,9 @@ namespace Avalonia.Data.Converters
                     );
 
             }
-            Action<object> action = null;
-            try
-            {
-                action = Expression
-                   .Lambda<Action<object>>(body, parameter)
-                   .Compile();
-            }
-            catch (Exception ex)
-            {
-                throw ex;
-            }
-            return action;
+            return Expression
+                .Lambda<Action<object>>(body, parameter)
+                .Compile();
         }
 
         static Func<object, bool> CreateCanExecute(object target

+ 3 - 4
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@@ -1,9 +1,6 @@
 using System;
-using System.Diagnostics;
 using System.IO;
 using System.Linq;
-using System.Reflection;
-using System.Threading;
 using Microsoft.Build.Framework;
 
 namespace Avalonia.Build.Tasks
@@ -41,7 +38,7 @@ namespace Avalonia.Build.Tasks
                 File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
                 ProjectDirectory, OutputPath, VerifyIl, outputImportance,
                 (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null,
-                EnableComInteropPatching, SkipXamlCompilation);
+                EnableComInteropPatching, SkipXamlCompilation, DebuggerLaunch);
             if (!res.Success)
                 return false;
             if (!res.WrittenFile)
@@ -87,5 +84,7 @@ namespace Avalonia.Build.Tasks
 
         public IBuildEngine BuildEngine { get; set; }
         public ITaskHost HostObject { get; set; }
+
+        public bool DebuggerLaunch { get; set; }
     }
 }

+ 43 - 9
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@@ -1,13 +1,10 @@
 using System;
-using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
-using System.Text;
 using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
 using Microsoft.Build.Framework;
 using Mono.Cecil;
-using Avalonia.Utilities;
 using Mono.Cecil.Cil;
 using Mono.Cecil.Rocks;
 using XamlX;
@@ -44,16 +41,23 @@ namespace Avalonia.Build.Tasks
             string projectDirectory,
             string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom,
             bool skipXamlCompilation)
+        {
+            return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false);
+        }
+
+        internal static CompileResult Compile(IBuildEngine engine, string input, string[] references,
+            string projectDirectory,
+            string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch)
         {
             var typeSystem = new CecilTypeSystem(references
                 .Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll"))
                 .Concat(new[] { input }), input);
-            
+
             var asm = typeSystem.TargetAssemblyDefinition;
 
             if (!skipXamlCompilation)
             {
-                var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance);
+                var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance, debuggerLaunch);
                 if (compileRes == null && !patchCom)
                     return new CompileResult(true);
                 if (compileRes == false)
@@ -62,7 +66,7 @@ namespace Avalonia.Build.Tasks
 
             if (patchCom)
                 ComInteropHelper.PatchAssembly(asm, typeSystem);
-            
+
             var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
             if (!string.IsNullOrWhiteSpace(strongNameKey))
                 writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
@@ -70,13 +74,43 @@ namespace Avalonia.Build.Tasks
             asm.Write(output, writerParameters);
 
             return new CompileResult(true, true);
-            
+
         }
-        
+
         static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem,
             string projectDirectory, bool verifyIl, 
-            MessageImportance logImportance)
+            MessageImportance logImportance
+            , bool debuggerLaunch = false)
         {
+            if (debuggerLaunch)
+            {
+                // According this https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debugger.launch?view=net-6.0#remarks
+                // documentation, on not windows platform Debugger.Launch() always return true without running a debugger.
+                if (System.Diagnostics.Debugger.Launch())
+                {
+                    // Set timeout at 1 minut.
+                    var time = new System.Diagnostics.Stopwatch();
+                    var timeout = TimeSpan.FromMinutes(1);
+                    time.Start();
+
+                    // wait for the debugger to be attacked or timeout.
+                    while (!System.Diagnostics.Debugger.IsAttached && time.Elapsed < timeout)
+                    {
+                        engine.LogMessage($"[PID:{System.Diagnostics.Process.GetCurrentProcess().Id}] Wating attach debugger. Elapsed {time.Elapsed}...", MessageImportance.High);
+                        System.Threading.Thread.Sleep(100);
+                    }
+
+                    time.Stop();                    
+                    if (time.Elapsed >= timeout)
+                    {
+                        engine.LogMessage("Wating attach debugger timeout.", MessageImportance.Normal);
+                    }
+                }
+                else
+                {
+                    engine.LogMessage("Debugging cancelled.", MessageImportance.Normal);
+                }
+            }
             var asm = typeSystem.TargetAssemblyDefinition;
             var emres = new EmbeddedResources(asm);
             var avares = new AvaloniaResources(asm, projectDirectory);

+ 2 - 0
src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs

@@ -85,7 +85,9 @@ namespace Avalonia.Controls.Platform
 
         public bool CurrentThreadIsLoopThread => TlsCurrentThreadIsLoopThread;
         public event Action<DispatcherPriority?> Signaled;
+#pragma warning disable CS0067
         public event Action<TimeSpan> Tick;
+#pragma warning restore CS0067
 
     }
 }

+ 0 - 3
src/Avalonia.Controls/Remote/RemoteServer.cs

@@ -15,9 +15,6 @@ namespace Avalonia.Controls.Remote
             public EmbeddableRemoteServerTopLevelImpl(IAvaloniaRemoteTransportConnection transport) : base(transport)
             {
             }
-#pragma warning disable 67
-            public Action LostFocus { get; set; }
-
         }
         
         public RemoteServer(IAvaloniaRemoteTransportConnection transport)

+ 1 - 1
src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

@@ -18,7 +18,7 @@ namespace Avalonia.Controls
 
         public bool SupportsSurroundingText => false;
         public TextInputMethodSurroundingText SurroundingText => throw new NotSupportedException();
-        public event EventHandler SurroundingTextChanged;
+        public event EventHandler SurroundingTextChanged { add { } remove { } }
         public string TextBeforeCursor => null;
         public string TextAfterCursor => null;
 

+ 1 - 1
src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs

@@ -59,7 +59,7 @@ namespace Avalonia.DesignerSupport.Remote
             remove { _onMessage -= value; }
         }
 
-        public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException;
+        public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException { add { } remove { } }
         public void Start()
         {
             UpdaterThread();

+ 13 - 12
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

@@ -17,16 +17,16 @@ namespace Avalonia.Diagnostics.ViewModels
     internal class ControlDetailsViewModel : ViewModelBase, IDisposable
     {
         private readonly IVisual _control;
-        private IDictionary<object, List<PropertyViewModel>> _propertyIndex;
+        private IDictionary<object, List<PropertyViewModel>>? _propertyIndex;
         private PropertyViewModel? _selectedProperty;
-        private DataGridCollectionView _propertiesView;
+        private DataGridCollectionView? _propertiesView;
         private bool _snapshotStyles;
         private bool _showInactiveStyles;
         private string? _styleStatus;
-        private object _selectedEntity;
+        private object? _selectedEntity;
         private readonly Stack<(string Name,object Entry)> _selectedEntitiesStack = new();
-        private string _selectedEntityName;
-        private string _selectedEntityType;
+        private string? _selectedEntityName;
+        private string? _selectedEntityType;
 
         public ControlDetailsViewModel(TreePageViewModel treePage, IVisual control)
         {
@@ -117,7 +117,7 @@ namespace Avalonia.Diagnostics.ViewModels
 
         public TreePageViewModel TreePage { get; }
 
-        public DataGridCollectionView PropertiesView
+        public DataGridCollectionView? PropertiesView
         {
             get => _propertiesView;
             private set => RaiseAndSetIfChanged(ref _propertiesView, value);
@@ -127,7 +127,7 @@ namespace Avalonia.Diagnostics.ViewModels
 
         public ObservableCollection<PseudoClassViewModel> PseudoClasses { get; }
 
-        public object SelectedEntity
+        public object? SelectedEntity
         {
             get => _selectedEntity;
             set
@@ -137,7 +137,7 @@ namespace Avalonia.Diagnostics.ViewModels
             }
         }
 
-        public string SelectedEntityName
+        public string? SelectedEntityName
         {
             get => _selectedEntityName;
             set
@@ -147,7 +147,7 @@ namespace Avalonia.Diagnostics.ViewModels
             }
         }
         
-        public string SelectedEntityType
+        public string? SelectedEntityType
         {
             get => _selectedEntityType;
             set
@@ -270,7 +270,7 @@ namespace Avalonia.Diagnostics.ViewModels
 
         private void ControlPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
         {
-            if (_propertyIndex.TryGetValue(e.Property, out var properties))
+            if (_propertyIndex is { } && _propertyIndex.TryGetValue(e.Property, out var properties))
             {
                 foreach (var property in properties)
                 {
@@ -284,6 +284,7 @@ namespace Avalonia.Diagnostics.ViewModels
         private void ControlPropertyChanged(object? sender, PropertyChangedEventArgs e)
         {
             if (e.PropertyName != null
+                && _propertyIndex is { }
                 && _propertyIndex.TryGetValue(e.PropertyName, out var properties))
             {
                 foreach (var property in properties)
@@ -402,7 +403,7 @@ namespace Avalonia.Diagnostics.ViewModels
             var selectedProperty = SelectedProperty;
             var selectedEntity = SelectedEntity;
             var selectedEntityName = SelectedEntityName;
-            if (selectedProperty == null)
+            if (selectedEntity == null ||  selectedProperty == null)
                 return;
 
             object? property;
@@ -419,7 +420,7 @@ namespace Avalonia.Diagnostics.ViewModels
                      ?.GetValue(selectedEntity);
             }
             if (property == null) return;
-            _selectedEntitiesStack.Push((Name:selectedEntityName,Entry:selectedEntity));
+            _selectedEntitiesStack.Push((Name:selectedEntityName!,Entry:selectedEntity));
             NavigateToProperty(property, selectedProperty.Name);
         }
 

+ 1 - 1
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@@ -15,7 +15,7 @@ namespace Avalonia.Diagnostics.ViewModels
             Nodes = nodes;
 
             PropertiesFilter = new FilterViewModel();
-            PropertiesFilter.RefreshFilter += (s, e) => Details?.PropertiesView.Refresh();
+            PropertiesFilter.RefreshFilter += (s, e) => Details?.PropertiesView?.Refresh();
 
             SettersFilter = new FilterViewModel();
             SettersFilter.RefreshFilter += (s, e) => Details?.UpdateStyleFilters();

+ 3 - 3
src/Avalonia.FreeDesktop/DBusMenuExporter.cs

@@ -413,10 +413,10 @@ namespace Avalonia.FreeDesktop
             #region Events
 
             private event Action<((int, IDictionary<string, object>)[] updatedProps, (int, string[])[] removedProps)>
-                ItemsPropertiesUpdated;
+                ItemsPropertiesUpdated { add { } remove { } }
             private event Action<(uint revision, int parent)> LayoutUpdated;
-            private event Action<(int id, uint timestamp)> ItemActivationRequested;
-            private event Action<PropertyChanges> PropertiesChanged;
+            private event Action<(int id, uint timestamp)> ItemActivationRequested { add { } remove { } }
+            private event Action<PropertyChanges> PropertiesChanged { add { } remove { } }
 
             async Task<IDisposable> IDBusMenu.WatchItemsPropertiesUpdatedAsync(Action<((int, IDictionary<string, object>)[] updatedProps, (int, string[])[] removedProps)> handler, Action<Exception> onError)
             {

+ 1 - 1
src/Avalonia.Native/AvaloniaNativeMenuExporter.cs

@@ -44,7 +44,7 @@ namespace Avalonia.Native
 
         public bool IsNativeMenuExported => _exported;
 
-        public event EventHandler OnIsNativeMenuExportedChanged;
+        public event EventHandler OnIsNativeMenuExportedChanged { add { } remove { } }
 
         public void SetNativeMenu(NativeMenu menu)
         {

+ 16 - 7
src/Avalonia.Themes.Default/Expander.xaml

@@ -15,7 +15,7 @@
                 BorderThickness="{TemplateBinding BorderThickness}"
                 CornerRadius="{TemplateBinding CornerRadius}">
           <Grid RowDefinitions="Auto,*">
-            <ToggleButton Name="PART_toggle" Grid.Row="0"  Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
+            <ToggleButton Name="PART_toggle" Grid.Row="0" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
             <ContentPresenter Name="PART_ContentPresenter"
                               Grid.Row="1"
                               IsVisible="{TemplateBinding IsExpanded}"
@@ -32,9 +32,12 @@
   <Style Selector="Expander[ExpandDirection=Up]">
     <Setter Property="Template">
       <ControlTemplate>
-        <Border Background="{TemplateBinding Background}">
+        <Border Background="{TemplateBinding Background}"
+                BorderBrush="{TemplateBinding BorderBrush}"
+                BorderThickness="{TemplateBinding BorderThickness}"
+                CornerRadius="{TemplateBinding CornerRadius}">
           <Grid RowDefinitions="*,Auto">
-            <ToggleButton Name="PART_toggle" Grid.Row="1"  Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
+            <ToggleButton Name="PART_toggle" Grid.Row="1" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
             <ContentPresenter Name="PART_ContentPresenter"
                               Grid.Row="0"
                               IsVisible="{TemplateBinding IsExpanded}"
@@ -51,9 +54,12 @@
   <Style Selector="Expander[ExpandDirection=Right]">
     <Setter Property="Template">
       <ControlTemplate>
-        <Border Background="{TemplateBinding Background}">
+        <Border Background="{TemplateBinding Background}"
+                BorderBrush="{TemplateBinding BorderBrush}"
+                BorderThickness="{TemplateBinding BorderThickness}"
+                CornerRadius="{TemplateBinding CornerRadius}">
           <Grid ColumnDefinitions="Auto,*">
-            <ToggleButton Name="PART_toggle" Grid.Column="0"  Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
+            <ToggleButton Name="PART_toggle" Grid.Column="0" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
             <ContentPresenter Name="PART_ContentPresenter"
                               Grid.Column="1"
                               IsVisible="{TemplateBinding IsExpanded}"
@@ -70,9 +76,12 @@
   <Style Selector="Expander[ExpandDirection=Left]">
     <Setter Property="Template">
       <ControlTemplate>
-        <Border Background="{TemplateBinding Background}">
+        <Border Background="{TemplateBinding Background}"
+                BorderBrush="{TemplateBinding BorderBrush}"
+                BorderThickness="{TemplateBinding BorderThickness}"
+                CornerRadius="{TemplateBinding CornerRadius}">
           <Grid ColumnDefinitions="*,Auto">
-            <ToggleButton Name="PART_toggle" Grid.Column="1"  Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
+            <ToggleButton Name="PART_toggle" Grid.Column="1" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
             <ContentPresenter Name="PART_ContentPresenter"
                               Grid.Column="0"
                               IsVisible="{TemplateBinding IsExpanded}"

+ 22 - 0
src/Avalonia.Visuals/Matrix.cs

@@ -215,6 +215,28 @@ namespace Avalonia
             return angle * 0.0174532925;
         }
 
+        /// <summary>
+        /// Appends another matrix as post-multiplication operation.
+        /// Equivalent to this * value;
+        /// </summary>
+        /// <param name="value">A matrix.</param>
+        /// <returns>Post-multiplied matrix.</returns>
+        public Matrix Append(Matrix value)
+        {
+            return this * value;
+        }
+
+        /// <summary>
+        /// Prpends another matrix as pre-multiplication operation.
+        /// Equivalent to value * this;
+        /// </summary>
+        /// <param name="value">A matrix.</param>
+        /// <returns>Pre-multiplied matrix.</returns>
+        public Matrix Prepend(Matrix value)
+        {
+            return value * this;
+        }
+
         /// <summary>
         /// Calculates the determinant for this matrix.
         /// </summary>

+ 5 - 5
src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs

@@ -18,11 +18,11 @@ namespace Avalonia.Media.Transformation
         public static Matrix ComposeTransform(Matrix.Decomposed decomposed)
         {
             // According to https://www.w3.org/TR/css-transforms-1/#recomposing-to-a-2d-matrix
-
-            return Matrix.CreateTranslation(decomposed.Translate) *
-                   Matrix.CreateRotation(decomposed.Angle) *
-                   Matrix.CreateSkew(decomposed.Skew.X, decomposed.Skew.Y) *
-                   Matrix.CreateScale(decomposed.Scale);
+            return Matrix.Identity
+                .Prepend(Matrix.CreateTranslation(decomposed.Translate))
+                .Prepend(Matrix.CreateRotation(decomposed.Angle))
+                .Prepend(Matrix.CreateSkew(decomposed.Skew.X, decomposed.Skew.Y))
+                .Prepend(Matrix.CreateScale(decomposed.Scale));
         }
 
         public static Matrix.Decomposed InterpolateDecomposedTransforms(ref Matrix.Decomposed from, ref Matrix.Decomposed to, double progress)

+ 4 - 1
src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs

@@ -86,6 +86,8 @@ namespace Avalonia.Media.Transformation
 
             if (fromIdentity && toIdentity)
             {
+                result.Matrix = Matrix.Identity;
+
                 return true;
             }
 
@@ -179,7 +181,8 @@ namespace Avalonia.Media.Transformation
                 }
                 case OperationType.Identity:
                 {
-                    // Do nothing.
+                    result.Matrix = Matrix.Identity;
+
                     break;
                 }
             }

+ 2 - 2
src/Avalonia.X11/X11Window.Xim.cs

@@ -112,8 +112,8 @@ namespace Avalonia.X11
             public ValueTask<bool> HandleEventAsync(RawKeyEventArgs args, int keyVal, int keyCode) =>
                 new ValueTask<bool>(false);
 
-            public event Action<string> Commit;
-            public event Action<X11InputMethodForwardedKey> ForwardKey;
+            public event Action<string> Commit { add { } remove { } }
+            public event Action<X11InputMethodForwardedKey> ForwardKey { add { } remove { } }
 
         }
         

+ 1 - 0
src/Avalonia.X11/X11Window.cs

@@ -1026,6 +1026,7 @@ namespace Avalonia.X11
             if (string.IsNullOrEmpty(title))
             {
                 XDeleteProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME);
+                XDeleteProperty(_x11.Display, _handle, _x11.Atoms.XA_WM_NAME);
             }
             else
             {

+ 67 - 0
tests/Avalonia.Animation.UnitTests/AnimatableTests.cs

@@ -1,5 +1,7 @@
 using System;
+using Avalonia.Animation.Animators;
 using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
 using Avalonia.Data;
 using Avalonia.Layout;
 using Avalonia.Media;
@@ -100,6 +102,71 @@ namespace Avalonia.Animation.UnitTests
                 Times.Never);
         }
 
+
+        [Theory]
+        [InlineData(null)] //null value
+        [InlineData("stringValue")] //string value
+        public void Invalid_Values_In_Animation_Should_Not_Crash_Animations(object invalidValue)
+        {
+            var keyframe1 = new KeyFrame()
+            {
+                Setters =
+                {
+                    new Setter(Layoutable.WidthProperty, 1d),
+                },
+                KeyTime = TimeSpan.FromSeconds(0)
+            };
+
+            var keyframe2 = new KeyFrame()
+            {
+                Setters =
+                {
+                    new Setter(Layoutable.WidthProperty, 2d),
+                },
+                KeyTime = TimeSpan.FromSeconds(2),
+            };
+
+            var keyframe3 = new KeyFrame()
+            {
+                Setters =
+                {
+                    new Setter(Layoutable.WidthProperty, invalidValue),
+                },
+                KeyTime = TimeSpan.FromSeconds(3),
+            };
+
+            var animation = new Animation()
+            {
+                Duration = TimeSpan.FromSeconds(3),
+                Children =
+                {
+                    keyframe1,
+                    keyframe2,
+                    keyframe3
+                },
+                IterationCount = new IterationCount(5),
+                PlaybackDirection = PlaybackDirection.Alternate,
+            };
+
+            var rect = new Rectangle()
+            {
+                Width = 11,
+            };
+
+            var originalValue = rect.Width;
+
+            var clock = new TestClock();
+            var animationRun = animation.RunAsync(rect, clock);
+
+            clock.Step(TimeSpan.Zero);
+            Assert.Equal(rect.Width, 1);
+            clock.Step(TimeSpan.FromSeconds(2));
+            Assert.Equal(rect.Width, 2);
+            clock.Step(TimeSpan.FromSeconds(3));
+            //here we have invalid value so value should be expected and set to initial original value
+            Assert.Equal(rect.Width, originalValue);
+        }
+
         [Fact]
         public void Transition_Is_Not_Applied_When_StyleTrigger_Changes_With_LocalValue_Present()
         {

+ 1 - 1
tests/Avalonia.Controls.UnitTests/ItemsSourceViewTests.cs

@@ -47,7 +47,7 @@ namespace Avalonia.Controls.UnitTests
 
         private class InvalidCollection : INotifyCollectionChanged, IEnumerable<string>
         {
-            public event NotifyCollectionChangedEventHandler CollectionChanged;
+            public event NotifyCollectionChangedEventHandler CollectionChanged { add { } remove { } }
 
             public IEnumerator<string> GetEnumerator()
             {

+ 70 - 2
tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs

@@ -129,7 +129,7 @@ namespace Avalonia.Visuals.UnitTests.Media
 
             Assert.Single(operations);
             Assert.Equal(TransformOperation.OperationType.Matrix, operations[0].Type);
-            
+
             var expectedMatrix = new Matrix(1, 2, 3, 4, 5, 6);
 
             Assert.Equal(expectedMatrix, operations[0].Matrix);
@@ -195,7 +195,7 @@ namespace Avalonia.Visuals.UnitTests.Media
         [Theory]
         [InlineData(0d, 10d)]
         [InlineData(0.5d, 15d)]
-        [InlineData(1d,20d)]
+        [InlineData(1d, 20d)]
         public void Can_Interpolate_Rotation(double progress, double angle)
         {
             var from = TransformOperations.Parse("rotate(10deg)");
@@ -225,5 +225,73 @@ namespace Avalonia.Visuals.UnitTests.Media
             Assert.Single(operations);
             Assert.Equal(TransformOperation.OperationType.Matrix, operations[0].Type);
         }
+
+        [Fact]
+        public void Order_Of_Operations_Is_Preserved_No_Prefix()
+        {
+            var from = TransformOperations.Parse("scale(1)");
+            var to = TransformOperations.Parse("translate(50px,50px) scale(0.5,0.5)");
+
+            var interpolated_0 = TransformOperations.Interpolate(from, to, 0);
+
+            Assert.True(interpolated_0.IsIdentity);
+
+            var interpolated_50 = TransformOperations.Interpolate(from, to, 0.5);
+
+            AssertMatrix(interpolated_50.Value, scaleX: 0.75, scaleY: 0.75, translateX: 12.5, translateY: 12.5);
+
+            var interpolated_100 = TransformOperations.Interpolate(from, to, 1);
+
+            AssertMatrix(interpolated_100.Value, scaleX: 0.5, scaleY: 0.5, translateX: 25, translateY: 25);
+        }
+
+        [Fact]
+        public void Order_Of_Operations_Is_Preserved_One_Prefix()
+        {
+            var from = TransformOperations.Parse("scale(1)");
+            var to = TransformOperations.Parse("scale(0.5,0.5) translate(50px,50px)");
+
+            var interpolated_0 = TransformOperations.Interpolate(from, to, 0);
+
+            Assert.True(interpolated_0.IsIdentity);
+
+            var interpolated_50 = TransformOperations.Interpolate(from, to, 0.5);
+
+            AssertMatrix(interpolated_50.Value, scaleX: 0.75, scaleY: 0.75, translateX: 25.0, translateY: 25);
+
+            var interpolated_100 = TransformOperations.Interpolate(from, to, 1);
+
+            AssertMatrix(interpolated_100.Value, scaleX: 0.5, scaleY: 0.5, translateX: 50, translateY: 50);
+        }
+
+        private static void AssertMatrix(Matrix matrix, double? angle = null, double? scaleX = null, double? scaleY = null, double? translateX = null, double? translateY = null)
+        {
+            Assert.True(Matrix.TryDecomposeTransform(matrix, out var composed));
+
+            if (angle.HasValue)
+            {
+                Assert.Equal(angle.Value, composed.Angle);
+            }
+
+            if (scaleX.HasValue)
+            {
+                Assert.Equal(scaleX.Value, composed.Scale.X);
+            }
+
+            if (scaleY.HasValue)
+            {
+                Assert.Equal(scaleY.Value, composed.Scale.Y);
+            }
+
+            if (translateX.HasValue)
+            {
+                Assert.Equal(translateX.Value, composed.Translate.X);
+            }
+
+            if (translateY.HasValue)
+            {
+                Assert.Equal(translateY.Value, composed.Translate.Y);
+            }
+        }
     }
 }