From d068e1b4fa0d2a94236b4ea659e9ba71a0b60ccf Mon Sep 17 00:00:00 2001 From: amwx Date: Sun, 2 May 2021 18:27:57 -0500 Subject: [PATCH 01/27] Clean fluent light --- .../Accents/AccentColors.xaml | 15 ++ src/Avalonia.Themes.Fluent/Accents/Base.xaml | 8 + .../Accents/BaseLight.xaml | 58 ++++- .../Accents/FluentBaseLight.xaml | 82 +++--- .../Accents/FluentControlResourcesLight.xaml | 242 ++++++++++++------ src/Avalonia.Themes.Fluent/FluentLight.xaml | 1 + 6 files changed, 260 insertions(+), 146 deletions(-) create mode 100644 src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml diff --git a/src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml b/src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml new file mode 100644 index 0000000000..1cf2f7e43d --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml @@ -0,0 +1,15 @@ + diff --git a/src/Avalonia.Themes.Fluent/Accents/Base.xaml b/src/Avalonia.Themes.Fluent/Accents/Base.xaml index 0e34eb3f3b..8597c76998 100644 --- a/src/Avalonia.Themes.Fluent/Accents/Base.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/Base.xaml @@ -22,5 +22,13 @@ 10,6,6,5 20 20 + + + 3 + 5 + + + scaleX(0.125) translateX(-2px) + scaleY(0.125) translateY(-2px) diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml index 6c8d16ddfa..d25d437e03 100644 --- a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml @@ -2,16 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard"> - - - #FF0078D7 - #FF005A9E - #FF004275 - #FF002642 - #FF429CE3 - #FF76B9ED - #FFA6D8FF - #FFFFFFFF #33FFFFFF @@ -40,6 +30,9 @@ #33000000 #C50500 + #17000000 + #2E000000 + @@ -135,7 +128,7 @@ - + @@ -150,9 +143,48 @@ - False + + + + + + + + + + + #FFFFFFFF + + + + 374 + 0,2,0,2 + 1 + -1,0,-1,0 + 32 + 64 + 456 + 0 + 1 + 0 + + 12,11,12,12 + 96 + 40 + 758 + + + 0 + + + 0,4,0,4 + + 12,0,12,0 + + + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml index ad23955776..7e048fe029 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml @@ -2,20 +2,9 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard"> - #17000000 - #2E000000 - - - - - - - - 3 - 5 - + - 1,1,1,1 + - - #FFFFFFFF - - + - + - + - + - 0 + - + - + - + - + - + - + - 12 + - XamlAutoFontFamily + - + - + - + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml index 5af24f57a8..e569b2a825 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml @@ -28,6 +28,7 @@ + - 1 @@ -53,6 +53,8 @@ + + 1 @@ -100,6 +103,7 @@ + - 21 - 64 - 80 - 240 + 64 1 - 1 - 0,0,0,4 - 2 - 11,5,11,7 - 11,11,11,13 - 11,11,11,13 + 1 + 11,5,11,7 Normal - SemiLight - @@ -147,16 +142,13 @@ - - - @@ -182,10 +174,26 @@ - + 0 + 0,4,0,4 + + 0 + - 0.6 + --> + + 4 0 + 1 @@ -276,7 +284,6 @@ - @@ -286,8 +293,46 @@ 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 - 0 @@ -361,7 +401,7 @@ - + @@ -393,6 +433,10 @@ + + - @@ -417,12 +460,9 @@ - - - @@ -430,11 +470,16 @@ - + @@ -482,6 +527,7 @@ + 4 2 0 - 0,0,0,4 Normal @@ -525,6 +570,8 @@ + 0 1 - - - - @@ -583,6 +626,13 @@ + + 12 @@ -613,16 +663,16 @@ + 8,5,8,7 + - @@ -631,17 +681,14 @@ - - - @@ -651,7 +698,13 @@ - + + + @@ -665,30 +718,35 @@ - - + + - XamlAutoFontFamily 24 - 40 - -25 12,0,12,0 12,0,12,0 SemiLight + + + @@ -704,23 +762,20 @@ - + + 0 - 1 - - - - @@ -729,18 +784,29 @@ - - - - + + + 16 + 8 + + + @@ -794,11 +857,15 @@ - - - 1 32 + + + + @@ -810,7 +877,12 @@ - + + + + + + diff --git a/src/Avalonia.Themes.Fluent/FluentLight.xaml b/src/Avalonia.Themes.Fluent/FluentLight.xaml index 1bc51f655e..31cff6def1 100644 --- a/src/Avalonia.Themes.Fluent/FluentLight.xaml +++ b/src/Avalonia.Themes.Fluent/FluentLight.xaml @@ -1,6 +1,7 @@ + From 7ff30a7d2b57ad6cc29fb871af2959a3c959c324 Mon Sep 17 00:00:00 2001 From: amwx Date: Sun, 2 May 2021 18:28:20 -0500 Subject: [PATCH 02/27] Clean fluent dark --- .../Accents/BaseDark.xaml | 70 +++-- .../Accents/FluentBaseDark.xaml | 84 +++--- .../Accents/FluentControlResourcesDark.xaml | 268 +++++++++++------- src/Avalonia.Themes.Fluent/FluentDark.xaml | 3 +- 4 files changed, 253 insertions(+), 172 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml index fb08b97b47..4c2022e2ce 100644 --- a/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml @@ -2,16 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard"> - - - #FF0078D7 - #FF005A9E - #FF004275 - #FF002642 - #FF429CE3 - #FF76B9ED - #FFA6D8FF - #FF000000 #33000000 @@ -40,6 +30,9 @@ #33FFFFFF #FFF000 + #18FFFFFF + #30FFFFFF + @@ -135,7 +128,7 @@ - + @@ -150,10 +143,45 @@ - False + + + + + + + + + #FF000000 + + + 374 + 0,2,0,2 + 1 + -1,0,-1,0 + 32 + 64 + 456 + 0 + 1 + 0 + 12,11,12,12 + 96 + 40 + 758 + + + 0 + + 0,4,0,4 + + + 12,0,12,0 + + + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml index 1c65911593..b4d4d93dc4 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml @@ -2,20 +2,9 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard"> - #18FFFFFF - #30FFFFFF - - - - - - - - 3 - 5 - + - 1,1,1,1 + + - #FF000000 - - + - + - + - + - 0 + + - + - + - + - + - + - + - 12 + - XamlAutoFontFamily + + - + - + - + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml index ae1fe42031..81ce7dfe1b 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml @@ -28,6 +28,7 @@ + - - 1 + @@ -53,6 +53,8 @@ + 1 @@ -101,6 +103,7 @@ + - 21 64 - 80 - 240 1 1 - 0,0,0,4 - 2 11,5,11,7 - 11,11,11,13 - 11,11,11,13 - Normal - SemiLight + Normal - - + @@ -147,17 +141,14 @@ - - + - - + - - + @@ -182,11 +173,32 @@ - - + + 0 + 0,4,0,4 + + 0 + + + + 4 + 0 + 1 @@ -268,8 +283,7 @@ - - + @@ -279,17 +293,45 @@ 1 - - - 0.6 - 4 - 0 - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 - 0 @@ -363,7 +400,7 @@ - + @@ -395,6 +432,10 @@ + + - @@ -419,12 +459,9 @@ - - - @@ -432,12 +469,17 @@ - - + + @@ -484,6 +526,7 @@ + 4 2 0 - 0,0,0,4 Normal @@ -527,6 +569,8 @@ + 0 1 - - - - @@ -585,6 +625,13 @@ + + 12 @@ -615,16 +662,16 @@ + 8,5,8,7 + - @@ -633,17 +680,14 @@ - - - @@ -654,6 +698,12 @@ + + @@ -667,30 +717,35 @@ - - + + - XamlAutoFontFamily 24 - 40 - -25 12,0,12,0 12,0,12,0 SemiLight - + + + @@ -705,24 +760,21 @@ - - + + + 0 - 1 - - - - @@ -731,20 +783,29 @@ - - - - + 16 + 8 + + + @@ -796,11 +856,14 @@ - - - 1 32 + + + @@ -812,7 +875,10 @@ - + + + + diff --git a/src/Avalonia.Themes.Fluent/FluentDark.xaml b/src/Avalonia.Themes.Fluent/FluentDark.xaml index 74b583a240..dbd25f5a1d 100644 --- a/src/Avalonia.Themes.Fluent/FluentDark.xaml +++ b/src/Avalonia.Themes.Fluent/FluentDark.xaml @@ -1,6 +1,7 @@ + xmlns:sys="clr-namespace:System;assembly=netstandard"> + From d0865f36d7b33a503787ef9b572e5c3304b33759 Mon Sep 17 00:00:00 2001 From: amwx Date: Sun, 2 May 2021 18:28:38 -0500 Subject: [PATCH 03/27] Various controls --- src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml | 8 +++++++- .../Controls/EmbeddableControlRoot.xaml | 2 +- src/Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml | 1 - src/Avalonia.Themes.Fluent/Controls/Menu.xaml | 1 - src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml | 3 +-- src/Avalonia.Themes.Fluent/Controls/Window.xaml | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml b/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml index a4e716c7f6..5110d70a80 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml @@ -1,4 +1,5 @@ diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml index d25d437e03..e5c0babb80 100644 --- a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml @@ -128,7 +128,6 @@ - @@ -143,8 +142,7 @@ - - + @@ -181,197 +179,5 @@ 12,0,12,0 - - - - diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml deleted file mode 100644 index 361f1f9038..0000000000 --- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml +++ /dev/null @@ -1,444 +0,0 @@ - diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml deleted file mode 100644 index 36c0feeb6f..0000000000 --- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml +++ /dev/null @@ -1,446 +0,0 @@ - diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml index 81ce7dfe1b..a1f423c059 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml @@ -28,17 +28,6 @@ - @@ -53,17 +42,6 @@ - 1 @@ -103,25 +81,6 @@ - 64 @@ -176,94 +135,15 @@ - 0 0,4,0,4 - - 0 - 4 - 0 - + 0 1 @@ -293,10 +173,7 @@ 1 - - - + @@ -310,7 +187,6 @@ - @@ -319,45 +195,19 @@ - - + - - + - - + 1 @@ -399,8 +249,7 @@ - - + @@ -432,24 +281,7 @@ - - + @@ -473,13 +305,7 @@ - - + @@ -526,23 +352,7 @@ - - + 4 2 @@ -569,31 +379,7 @@ - - + 0 1 @@ -626,36 +412,6 @@ - - 12 1 @@ -663,10 +419,6 @@ 8,5,8,7 - @@ -697,12 +449,7 @@ - - + @@ -726,26 +473,13 @@ - - 24 12,0,12,0 12,0,12,0 SemiLight - - - + @@ -763,8 +497,6 @@ - 0 @@ -795,41 +527,6 @@ 16 8 - - @@ -859,11 +556,6 @@ 1 32 - - @@ -878,7 +570,6 @@ - - + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml index e569b2a825..f75af76144 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml @@ -28,18 +28,7 @@ - - + @@ -53,18 +42,7 @@ - - + 1 @@ -103,26 +81,7 @@ - - + 64 1 @@ -152,7 +111,6 @@ - @@ -176,95 +134,16 @@ - 0 0,4,0,4 - - 0 - 4 0 - - + 1 32 @@ -293,12 +172,8 @@ 1 - - - + - @@ -309,8 +184,7 @@ - - + @@ -319,8 +193,7 @@ - - + @@ -328,37 +201,10 @@ - - - - + 1 @@ -400,8 +246,7 @@ - - + @@ -433,25 +278,7 @@ - - - + @@ -474,13 +301,7 @@ - - + @@ -527,23 +348,7 @@ - - + 4 2 @@ -570,31 +375,7 @@ - - + 0 1 @@ -627,36 +408,6 @@ - - 12 1 @@ -664,11 +415,7 @@ 8,5,8,7 - - + @@ -698,13 +445,7 @@ - - - + @@ -726,27 +467,14 @@ - - - + 24 12,0,12,0 12,0,12,0 SemiLight - - - + @@ -763,10 +491,7 @@ - - - + 0 @@ -796,42 +521,7 @@ 16 8 - - - + @@ -860,12 +550,6 @@ 1 32 - - - @@ -881,8 +565,5 @@ - - - From dfa75a6d4f5623d82bf3babe485e04a22d3425b9 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Fri, 7 May 2021 23:21:00 +0800 Subject: [PATCH 06/27] fix bug on instances where SCB animation applies a localvalue wrongly. --- .../Animators/SolidColorBrushAnimator.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index a8e618af27..42962e219e 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -41,23 +41,23 @@ namespace Avalonia.Animation.Animators SolidColorBrush finalTarget; var targetVal = control.GetValue(Property); - if (targetVal is null) + + switch (targetVal) { - finalTarget = new SolidColorBrush(Colors.Transparent); - control.SetValue(Property, finalTarget); - } - else if (targetVal is ImmutableSolidColorBrush immutableSolidColorBrush) - { - finalTarget = new SolidColorBrush(immutableSolidColorBrush.Color); - control.SetValue(Property, finalTarget); - } - else if (targetVal is ISolidColorBrush) - { - finalTarget = targetVal as SolidColorBrush; - } - else - { - return Disposable.Empty; + case null: + finalTarget = new SolidColorBrush(Colors.Transparent); + break; + case ImmutableSolidColorBrush immutableSolidColorBrush: + finalTarget = new SolidColorBrush(immutableSolidColorBrush.Color); + break; + case SolidColorBrush target: + finalTarget = target; + break; + case ISolidColorBrush target: + finalTarget = target as SolidColorBrush; + break; + default: + return Disposable.Empty; } if (_colorAnimator == null) From f80709287715d96cf9e3c85493d168b228aec05a Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sat, 8 May 2021 19:58:57 +0800 Subject: [PATCH 07/27] Heavily simplify SCB animator --- .../Animation/Animators/ColorAnimator.cs | 7 ++- .../Animators/SolidColorBrushAnimator.cs | 60 ++----------------- 2 files changed, 11 insertions(+), 56 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs index 6d1f6c39bd..1686cafeb9 100644 --- a/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs @@ -31,6 +31,11 @@ namespace Avalonia.Animation.Animators } public override Color Interpolate(double progress, Color oldValue, Color newValue) + { + return InterpolateCore(progress, oldValue, newValue); + } + + internal static Color InterpolateCore(double progress, Color oldValue, Color newValue) { // normalize sRGB values. var oldA = oldValue.A / 255d; @@ -59,7 +64,7 @@ namespace Avalonia.Animation.Animators var b = oldB + progress * (newB - oldB); // convert back to sRGB in the [0..255] range - a = a * 255d; + a *= 255d; r = OECF_sRGB(r) * 255d; g = OECF_sRGB(g) * 255d; b = OECF_sRGB(b) * 255d; diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index 42962e219e..80eb13d2c4 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -8,64 +8,14 @@ namespace Avalonia.Animation.Animators /// /// Animator that handles . /// - public class SolidColorBrushAnimator : Animator + public class SolidColorBrushAnimator : Animator { - private ColorAnimator _colorAnimator; - - private void InitializeColorAnimator() - { - _colorAnimator = new ColorAnimator(); - - foreach (AnimatorKeyFrame keyframe in this) - { - _colorAnimator.Add(keyframe); - } - - _colorAnimator.Property = SolidColorBrush.ColorProperty; - } - - public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable match, Action onComplete) + public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) { - // Preprocess keyframe values to Color if the xaml parser converts them to ISCB. - foreach (var keyframe in this) - { - if (keyframe.Value is ISolidColorBrush colorBrush) - { - keyframe.Value = colorBrush.Color; - } - else - { - return Disposable.Empty; - } - } - - SolidColorBrush finalTarget; - var targetVal = control.GetValue(Property); + if (oldValue is not ISolidColorBrush oldValS || newValue is not ISolidColorBrush newValS) + return Brushes.Transparent; - switch (targetVal) - { - case null: - finalTarget = new SolidColorBrush(Colors.Transparent); - break; - case ImmutableSolidColorBrush immutableSolidColorBrush: - finalTarget = new SolidColorBrush(immutableSolidColorBrush.Color); - break; - case SolidColorBrush target: - finalTarget = target; - break; - case ISolidColorBrush target: - finalTarget = target as SolidColorBrush; - break; - default: - return Disposable.Empty; - } - - if (_colorAnimator == null) - InitializeColorAnimator(); - - return _colorAnimator.Apply(animation, finalTarget, clock ?? control.Clock, match, onComplete); + return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValS.Color, newValS.Color)); } - - public override SolidColorBrush Interpolate(double p, SolidColorBrush o, SolidColorBrush n) => null; } } From 8df4a59cbe9c133860a2197f4896c8122c9cf638 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sat, 8 May 2021 21:15:01 +0800 Subject: [PATCH 08/27] dont use "not" c# preview feature --- .../Animation/Animators/SolidColorBrushAnimator.cs | 2 +- src/Avalonia.Visuals/ApiCompatBaseline.txt | 4 +++- src/Avalonia.Visuals/Media/SolidColorBrush.cs | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index 80eb13d2c4..3ae9b3be5f 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -12,7 +12,7 @@ namespace Avalonia.Animation.Animators { public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) { - if (oldValue is not ISolidColorBrush oldValS || newValue is not ISolidColorBrush newValS) + if (!(oldValue is ISolidColorBrush oldValS) || !(newValue is ISolidColorBrush newValS)) return Brushes.Transparent; return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValS.Color, newValS.Color)); diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 35ba8f2b19..69a0ea30fa 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -1,4 +1,6 @@ Compat issues with assembly Avalonia.Visuals: +CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Animation.Animators.SolidColorBrushAnimator' does not inherit from base type 'Avalonia.Animation.Animators.Animator' in the implementation but it does in the contract. +MembersMustExist : Member 'public Avalonia.Media.SolidColorBrush Avalonia.Animation.Animators.SolidColorBrushAnimator.Interpolate(System.Double, Avalonia.Media.SolidColorBrush, Avalonia.Media.SolidColorBrush)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. CannotSealType : Type 'Avalonia.Media.TextFormatting.GenericTextParagraphProperties' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. @@ -63,4 +65,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' is present in the contract but not in the implementation. MembersMustExist : Member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' does not exist in the implementation but it does exist in the contract. -Total Issues: 64 +Total Issues: 66 diff --git a/src/Avalonia.Visuals/Media/SolidColorBrush.cs b/src/Avalonia.Visuals/Media/SolidColorBrush.cs index 8e30880489..f390489ed5 100644 --- a/src/Avalonia.Visuals/Media/SolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/SolidColorBrush.cs @@ -17,8 +17,8 @@ namespace Avalonia.Media static SolidColorBrush() { - Animation.Animation.RegisterAnimator(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType)); - AffectsRender(ColorProperty); + Animation.Animation.RegisterAnimator(prop => typeof(ISolidColorBrush).IsAssignableFrom(prop.PropertyType)); + AffectsRender(ColorProperty); } /// From 80c276f31e7e1c0ca5fa5eb60a44396f6f1cfe45 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sun, 9 May 2021 00:15:56 +0800 Subject: [PATCH 09/27] Revamp the brush animators to handle future IBrush types. --- src/Avalonia.Animation/Animation.cs | 11 +++ .../Animation/Animators/BaseBrushAnimator.cs | 68 +++++++++++++++++++ .../Animators/SolidColorBrushAnimator.cs | 12 ++-- src/Avalonia.Visuals/Media/Brush.cs | 2 + src/Avalonia.Visuals/Media/SolidColorBrush.cs | 5 +- 5 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs index 05142532e9..c42153ec4f 100644 --- a/src/Avalonia.Animation/Animation.cs +++ b/src/Avalonia.Animation/Animation.cs @@ -209,6 +209,17 @@ namespace Avalonia.Animation ( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator) ), }; + /// + /// Registers a that can handle + /// a value type that matches the specified condition. + /// + /// + /// The condition to which the + /// is to be activated and used. + /// + /// + /// The type of the animator to instantiate. + /// public static void RegisterAnimator(Func condition) where TAnimator : IAnimator { diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs new file mode 100644 index 0000000000..9576803692 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using Avalonia.Logging; +using Avalonia.Media; + +namespace Avalonia.Animation.Animators +{ + /// + /// Animator that handles all animations on properties + /// with as their type and + /// redirect them to the properly registered + /// animators in this class. + /// + public class BaseBrushAnimator : Animator + { + private IAnimator _targetAnimator; + + private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators = new(); + + /// + /// Register an that handles a specific + /// 's descendant value type. + /// + /// + /// The condition to which the + /// is to be activated and used. + /// + /// + /// The type of the animator to instantiate. + /// + public static void RegisterBrushAnimator(Func condition) + where TAnimator : IAnimator + { + _brushAnimators.Insert(0, (condition, typeof(TAnimator))); + } + + /// + public override IDisposable Apply(Animation animation, Animatable control, IClock clock, + IObservable match, Action onComplete) + { + foreach (var valueType in _brushAnimators + .Where(valueType => valueType.Match(this[0].Value.GetType()))) + { + _targetAnimator = (IAnimator)Activator.CreateInstance(valueType.AnimatorType); + + foreach (var keyframe in this) + { + _targetAnimator.Add(keyframe); + } + + _targetAnimator.Property = this.Property; + + _targetAnimator.Apply(animation, control, clock, match, onComplete); + } + + Logger.TryGet(LogEventLevel.Error, LogArea.Animations)?.Log( + this, + "The animation's keyframe values didn't match any brush animators registered in BaseBrushAnimator."); + + return Disposable.Empty; + } + + /// + public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) => null; + } +} diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index 3ae9b3be5f..9077ef2e6c 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -1,21 +1,19 @@ -using System; -using System.Reactive.Disposables; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Media.Immutable; namespace Avalonia.Animation.Animators { /// - /// Animator that handles . + /// Animator that handles values. /// public class SolidColorBrushAnimator : Animator { public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) { - if (!(oldValue is ISolidColorBrush oldValS) || !(newValue is ISolidColorBrush newValS)) + if (!(oldValue is ISolidColorBrush oldSCB) || !(newValue is ISolidColorBrush newSCB)) return Brushes.Transparent; - - return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValS.Color, newValS.Color)); + + return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldSCB.Color, newSCB.Color)); } } } diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index a19d5af8b7..fb03d19a4e 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using Avalonia.Animation; +using Avalonia.Animation.Animators; namespace Avalonia.Media { @@ -21,6 +22,7 @@ namespace Avalonia.Media static Brush() { + Animation.Animation.RegisterAnimator(prop => typeof(IBrush).IsAssignableFrom(prop.PropertyType)); AffectsRender(OpacityProperty); } diff --git a/src/Avalonia.Visuals/Media/SolidColorBrush.cs b/src/Avalonia.Visuals/Media/SolidColorBrush.cs index f390489ed5..32663bb8eb 100644 --- a/src/Avalonia.Visuals/Media/SolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/SolidColorBrush.cs @@ -1,4 +1,3 @@ -using Avalonia.Animation; using Avalonia.Animation.Animators; using Avalonia.Media.Immutable; @@ -17,8 +16,8 @@ namespace Avalonia.Media static SolidColorBrush() { - Animation.Animation.RegisterAnimator(prop => typeof(ISolidColorBrush).IsAssignableFrom(prop.PropertyType)); - AffectsRender(ColorProperty); + BaseBrushAnimator.RegisterBrushAnimator(match => typeof(ISolidColorBrush).IsAssignableFrom(match)); + AffectsRender(ColorProperty); } /// From 72ae35c9ea61745e238e7aa770bc59e5d337ea61 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sun, 9 May 2021 00:16:49 +0800 Subject: [PATCH 10/27] return after applying the target animator in BaseBrushAnimator.cs --- src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs index 9576803692..30162999b5 100644 --- a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs @@ -52,7 +52,7 @@ namespace Avalonia.Animation.Animators _targetAnimator.Property = this.Property; - _targetAnimator.Apply(animation, control, clock, match, onComplete); + return _targetAnimator.Apply(animation, control, clock, match, onComplete); } Logger.TryGet(LogEventLevel.Error, LogArea.Animations)?.Log( From da907379054ab2d6170f8cfcc5905e7026131fe4 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sun, 9 May 2021 00:22:25 +0800 Subject: [PATCH 11/27] revert to non preview feature again --- src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs index 30162999b5..c9a7ad9cba 100644 --- a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs @@ -17,7 +17,7 @@ namespace Avalonia.Animation.Animators { private IAnimator _targetAnimator; - private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators = new(); + private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators = new List<(Func Match, Type AnimatorType)>(); /// /// Register an that handles a specific From 77361a069fa5289b7efb7a40f0f52609c38790e0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 8 May 2021 22:29:24 -0400 Subject: [PATCH 12/27] Theme loading benchmark --- .../Themes/ThemeBenchmark.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs diff --git a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs new file mode 100644 index 0000000000..79a11d0cea --- /dev/null +++ b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs @@ -0,0 +1,63 @@ +using System; + +using Avalonia.Controls; +using Avalonia.Markup.Xaml.Styling; +using Avalonia.Shared.PlatformSupport; +using Avalonia.Styling; +using Avalonia.UnitTests; + +using BenchmarkDotNet.Attributes; + +namespace Avalonia.Benchmarks.Themes +{ + [MemoryDiagnoser] + public class ThemeBenchmark : IDisposable + { + private IDisposable _app; + + public ThemeBenchmark() + { + AssetLoader.RegisterResUriParsers(); + + _app = UnitTestApplication.Start(TestServices.StyledWindow.With(theme: () => null)); + // Add empty style to override it later + UnitTestApplication.Current.Styles.Add(new Style()); + } + + [Benchmark] + [Arguments("avares://Avalonia.Themes.Fluent/FluentDark.xaml")] + [Arguments("avares://Avalonia.Themes.Fluent/FluentLight.xaml")] + public bool InitFluentTheme(string themeUri) + { + UnitTestApplication.Current.Styles[0] = new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Benchmarks")) + { + Source = new Uri(themeUri) + }; + return ((IResourceHost)UnitTestApplication.Current).TryGetResource("SystemAccentColor", out _); + } + + [Benchmark] + [Arguments("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml")] + [Arguments("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml")] + public bool InitDefaultTheme(string themeUri) + { + UnitTestApplication.Current.Styles[0] = new Styles + { + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Benchmarks")) + { + Source = new Uri(themeUri) + }, + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Benchmarks")) + { + Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml") + } + }; + return ((IResourceHost)UnitTestApplication.Current).TryGetResource("ThemeAccentColor", out _); + } + + public void Dispose() + { + _app.Dispose(); + } + } +} From b06daeb9cd40a30ae3ca45b331bd4bf928783557 Mon Sep 17 00:00:00 2001 From: amwx Date: Sat, 8 May 2021 22:32:30 -0500 Subject: [PATCH 13/27] Remove commented line --- src/Avalonia.Themes.Fluent/FluentDark.xaml | 1 - src/Avalonia.Themes.Fluent/FluentLight.xaml | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/FluentDark.xaml b/src/Avalonia.Themes.Fluent/FluentDark.xaml index c54b5c5e7d..50b12efd78 100644 --- a/src/Avalonia.Themes.Fluent/FluentDark.xaml +++ b/src/Avalonia.Themes.Fluent/FluentDark.xaml @@ -4,7 +4,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/FluentLight.xaml b/src/Avalonia.Themes.Fluent/FluentLight.xaml index b2cb04163c..feb043c5f3 100644 --- a/src/Avalonia.Themes.Fluent/FluentLight.xaml +++ b/src/Avalonia.Themes.Fluent/FluentLight.xaml @@ -4,7 +4,6 @@ - From d084cf8fc7d2bf35fcf387eac3bf4b9f17cdbc9a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 10 May 2021 14:49:54 +0100 Subject: [PATCH 14/27] dont break api. --- src/Avalonia.Animation/AnimationInstance`1.cs | 2 +- .../Animators/Animator`1.cs | 2 +- .../Animators/SolidColorBrushAnimator.cs | 21 ++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index 6f601a3e13..77547451bb 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -12,7 +12,7 @@ namespace Avalonia.Animation /// Handles interpolation and time-related functions /// for keyframe animations. /// - internal class AnimationInstance : SingleSubscriberObservableBase + internal class AnimationInstance : SingleSubscriberObservableBase { private T _lastInterpValue; private T _firstKFValue; diff --git a/src/Avalonia.Animation/Animators/Animator`1.cs b/src/Avalonia.Animation/Animators/Animator`1.cs index 0660440e30..679c879935 100644 --- a/src/Avalonia.Animation/Animators/Animator`1.cs +++ b/src/Avalonia.Animation/Animators/Animator`1.cs @@ -116,7 +116,7 @@ namespace Avalonia.Animation.Animators clock ?? control.Clock ?? Clock.GlobalClock, onComplete, InterpolationHandler); - return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); + return control.Bind(Property, instance, BindingPriority.Animation); } /// diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index 9077ef2e6c..dd1580e3b3 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -1,4 +1,5 @@ -using Avalonia.Media; +using System; +using Avalonia.Media; using Avalonia.Media.Immutable; namespace Avalonia.Animation.Animators @@ -6,14 +7,20 @@ namespace Avalonia.Animation.Animators /// /// Animator that handles values. /// - public class SolidColorBrushAnimator : Animator + public class ISolidColorBrushAnimator : Animator { - public override IBrush Interpolate(double progress, IBrush oldValue, IBrush newValue) + public override ISolidColorBrush Interpolate(double progress, ISolidColorBrush oldValue, ISolidColorBrush newValue) { - if (!(oldValue is ISolidColorBrush oldSCB) || !(newValue is ISolidColorBrush newSCB)) - return Brushes.Transparent; - - return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldSCB.Color, newSCB.Color)); + return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color)); + } + } + + [Obsolete] + public class SolidColorBrushAnimator : Animator + { + public override SolidColorBrush Interpolate(double progress, SolidColorBrush oldValue, SolidColorBrush newValue) + { + return new SolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color)); } } } From 545253290ed35aa05b858e99a98c466bf5585f92 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 10 May 2021 14:52:46 +0100 Subject: [PATCH 15/27] register new brush animator. --- src/Avalonia.Visuals/Media/SolidColorBrush.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/SolidColorBrush.cs b/src/Avalonia.Visuals/Media/SolidColorBrush.cs index 32663bb8eb..fd94cbd214 100644 --- a/src/Avalonia.Visuals/Media/SolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/SolidColorBrush.cs @@ -16,7 +16,7 @@ namespace Avalonia.Media static SolidColorBrush() { - BaseBrushAnimator.RegisterBrushAnimator(match => typeof(ISolidColorBrush).IsAssignableFrom(match)); + BaseBrushAnimator.RegisterBrushAnimator(match => typeof(ISolidColorBrush).IsAssignableFrom(match)); AffectsRender(ColorProperty); } From c6f248a1e5ade8744440aa4acbc2094afbdad9f7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 10 May 2021 14:55:16 +0100 Subject: [PATCH 16/27] fix api compat --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 69a0ea30fa..35ba8f2b19 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -1,6 +1,4 @@ Compat issues with assembly Avalonia.Visuals: -CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Animation.Animators.SolidColorBrushAnimator' does not inherit from base type 'Avalonia.Animation.Animators.Animator' in the implementation but it does in the contract. -MembersMustExist : Member 'public Avalonia.Media.SolidColorBrush Avalonia.Animation.Animators.SolidColorBrushAnimator.Interpolate(System.Double, Avalonia.Media.SolidColorBrush, Avalonia.Media.SolidColorBrush)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. CannotSealType : Type 'Avalonia.Media.TextFormatting.GenericTextParagraphProperties' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. @@ -65,4 +63,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' is present in the contract but not in the implementation. MembersMustExist : Member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' does not exist in the implementation but it does exist in the contract. -Total Issues: 66 +Total Issues: 64 From e63e00f7dde218280785c61689cabb83a27d606a Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Mon, 10 May 2021 22:06:07 +0800 Subject: [PATCH 17/27] remove linq --- .../Animation/Animators/BaseBrushAnimator.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs index c9a7ad9cba..508891fd72 100644 --- a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reactive.Disposables; using Avalonia.Logging; using Avalonia.Media; @@ -17,7 +16,8 @@ namespace Avalonia.Animation.Animators { private IAnimator _targetAnimator; - private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators = new List<(Func Match, Type AnimatorType)>(); + private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators = + new List<(Func Match, Type AnimatorType)>(); /// /// Register an that handles a specific @@ -35,14 +35,15 @@ namespace Avalonia.Animation.Animators { _brushAnimators.Insert(0, (condition, typeof(TAnimator))); } - + /// public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable match, Action onComplete) { - foreach (var valueType in _brushAnimators - .Where(valueType => valueType.Match(this[0].Value.GetType()))) + foreach (var valueType in _brushAnimators) { + if (!valueType.Match(this[0].Value.GetType())) continue; + _targetAnimator = (IAnimator)Activator.CreateInstance(valueType.AnimatorType); foreach (var keyframe in this) From 472e179e41e7f108baa398fec043d516a0e0a743 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 10 May 2021 16:22:54 +0100 Subject: [PATCH 18/27] no boxing required. --- src/Avalonia.Animation/AnimationInstance`1.cs | 2 +- src/Avalonia.Animation/Animators/Animator`1.cs | 8 +++++++- .../Animation/Animators/SolidColorBrushAnimator.cs | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index 77547451bb..6f601a3e13 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -12,7 +12,7 @@ namespace Avalonia.Animation /// Handles interpolation and time-related functions /// for keyframe animations. /// - internal class AnimationInstance : SingleSubscriberObservableBase + internal class AnimationInstance : SingleSubscriberObservableBase { private T _lastInterpValue; private T _firstKFValue; diff --git a/src/Avalonia.Animation/Animators/Animator`1.cs b/src/Avalonia.Animation/Animators/Animator`1.cs index 679c879935..d784227620 100644 --- a/src/Avalonia.Animation/Animators/Animator`1.cs +++ b/src/Avalonia.Animation/Animators/Animator`1.cs @@ -104,6 +104,11 @@ namespace Avalonia.Animation.Animators throw new Exception("Index time is out of keyframe time range."); } + public virtual IDisposable BindAnimation(Animatable control, IObservable instance) + { + return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); + } + /// /// Runs the KeyFrames Animation. /// @@ -116,7 +121,8 @@ namespace Avalonia.Animation.Animators clock ?? control.Clock ?? Clock.GlobalClock, onComplete, InterpolationHandler); - return control.Bind(Property, instance, BindingPriority.Animation); + + return BindAnimation(control, instance); } /// diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs index dd1580e3b3..cec96fecf8 100644 --- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Data; using Avalonia.Media; using Avalonia.Media.Immutable; @@ -13,6 +14,11 @@ namespace Avalonia.Animation.Animators { return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color)); } + + public override IDisposable BindAnimation(Animatable control, IObservable instance) + { + return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); + } } [Obsolete] From 73556f8a4232eb33095837f5b68508515a1f53a2 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 10 May 2021 23:18:10 +0200 Subject: [PATCH 19/27] Added failing DrawingContextImpl tests. Skia `DrawingContextImpl` throws a NRE when passed a pen with 0 thickness. --- .../DrawingContextImplTests.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs diff --git a/tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs b/tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs new file mode 100644 index 0000000000..df0cc2fc1a --- /dev/null +++ b/tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs @@ -0,0 +1,30 @@ +using Avalonia.Media; +using Avalonia.Skia.Helpers; +using SkiaSharp; +using Xunit; + +namespace Avalonia.Skia.UnitTests +{ + public class DrawingContextImplTests + { + [Fact] + public void DrawLine_With_Zero_Thickness_Pen_Does_Not_Throw() + { + var target = CreateTarget(); + target.DrawLine(new Pen(Brushes.Black, 0), new Point(0, 0), new Point(10, 10)); + } + + [Fact] + public void DrawRectangle_With_Zero_Thickness_Pen_Does_Not_Throw() + { + var target = CreateTarget(); + target.DrawRectangle(Brushes.Black, new Pen(Brushes.Black, 0), new RoundedRect(new Rect(0, 0, 100, 100), new CornerRadius(4))); + } + + private DrawingContextImpl CreateTarget() + { + var canvas = new SKCanvas(new SKBitmap(100, 100)); + return (DrawingContextImpl)DrawingContextHelper.WrapSkiaCanvas(canvas, new Vector(96, 96)); + } + } +} From 64e0fb7f241d4f25c32f13ef680538518ad63e9b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 10 May 2021 23:21:50 +0200 Subject: [PATCH 20/27] Check for null paint. `CreatePaint` when passed a `Pen` can return `default` which means that `paint.paint` will be null. Check for this before trying to use it. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 22 ++++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index b7d5d3ec59..2c5c2acd52 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -166,7 +166,10 @@ namespace Avalonia.Skia { using (var paint = CreatePaint(_strokePaint, pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y)))) { - Canvas.DrawLine((float) p1.X, (float) p1.Y, (float) p2.X, (float) p2.Y, paint.Paint); + if (paint.Paint is object) + { + Canvas.DrawLine((float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, paint.Paint); + } } } @@ -361,7 +364,6 @@ namespace Avalonia.Skia { Canvas.DrawRect(rc, paint.Paint); } - } } @@ -397,15 +399,17 @@ namespace Avalonia.Skia { using (var paint = CreatePaint(_strokePaint, pen, rect.Rect.Size)) { - if (isRounded) - { - Canvas.DrawRoundRect(skRoundRect, paint.Paint); - } - else + if (paint.Paint is object) { - Canvas.DrawRect(rc, paint.Paint); + if (isRounded) + { + Canvas.DrawRoundRect(skRoundRect, paint.Paint); + } + else + { + Canvas.DrawRect(rc, paint.Paint); + } } - } } } From d1192901a1aadbe20abb669cb9f26363b0cb8bd3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 12 May 2021 18:20:21 +0100 Subject: [PATCH 21/27] add contentattribute to image. --- src/Avalonia.Controls/Image.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 5fc7d8b6b6..247b62d3cf 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -1,5 +1,6 @@ using Avalonia.Media; using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -37,6 +38,7 @@ namespace Avalonia.Controls /// /// Gets or sets the image that will be displayed. /// + [Content] public IImage Source { get { return GetValue(SourceProperty); } From 6fc5af3a68dd22130de3d6c6565ec8003d282589 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 12 May 2021 20:24:13 +0100 Subject: [PATCH 22/27] use datatemplates instead of itemstemplate for datavalidationerrors. --- src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml index b513fd5eae..b1c8650292 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml @@ -28,13 +28,13 @@ - + - + From b0d5e3197c1580af8aff283ce9ef9778eb4ef9bc Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 13 May 2021 01:00:34 -0400 Subject: [PATCH 23/27] Include inner exception when throwing from BindingEntry --- src/Avalonia.Base/PropertyStore/BindingEntry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs index 1b29338f07..362736eb06 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs @@ -79,7 +79,7 @@ namespace Avalonia.PropertyStore public void OnError(Exception error) { - throw new NotImplementedException(); + throw new NotImplementedException("BindingEntry.OnError is not implemented", error); } public void OnNext(BindingValue value) From 2043ed3a30ca9feab071f7683b403b1bf059d912 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 13 May 2021 10:26:22 +0100 Subject: [PATCH 24/27] dont provide any templates for datavalidation errors. --- .../Controls/DataValidationErrors.xaml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml index b1c8650292..53c5d4eda6 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml @@ -27,14 +27,12 @@ + From b3e2b05d1ffefe52fa5f3a69ab316b6e11f55fe0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 13 May 2021 13:28:20 +0200 Subject: [PATCH 25/27] Implement StretchDirection for Viewbox. Cleanup Viewbox. --- samples/ControlCatalog/Pages/ViewboxPage.xaml | 28 ++++++- .../ControlCatalog/Pages/ViewboxPage.xaml.cs | 31 ++++++- src/Avalonia.Controls/Image.cs | 4 +- src/Avalonia.Controls/Viewbox.cs | 81 +++++++------------ .../ViewboxTests.cs | 56 +++++++++++++ 5 files changed, 141 insertions(+), 59 deletions(-) diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index e78cf2bc22..f8b4a62290 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -22,14 +22,38 @@ - + Viewbox A control used to scale single child. + + + + + + + + + + + + + + + + + + + + + + + None Fill diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs index 1b5f4bc7f4..7a2bceb8f2 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs @@ -1,18 +1,47 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Media; namespace ControlCatalog.Pages { public class ViewboxPage : UserControl { + private readonly Viewbox _viewbox; + private readonly ComboBox _stretchSelector; + public ViewboxPage() { - this.InitializeComponent(); + InitializeComponent(); + + _viewbox = this.FindControl("Viewbox"); + + _stretchSelector = this.FindControl("StretchSelector"); + + _stretchSelector.Items = new[] + { + Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None + }; + + _stretchSelector.SelectedIndex = 0; + + var stretchDirectionSelector = this.FindControl("StretchDirectionSelector"); + + stretchDirectionSelector.Items = new[] + { + StretchDirection.Both, StretchDirection.DownOnly, StretchDirection.UpOnly + }; + + stretchDirectionSelector.SelectedIndex = 0; } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } + + private void StretchSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + _viewbox.Stretch = (Stretch) _stretchSelector.SelectedItem!; + } } } diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 247b62d3cf..c448729643 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -31,8 +31,8 @@ namespace Avalonia.Controls static Image() { - AffectsRender(SourceProperty, StretchProperty); - AffectsMeasure(SourceProperty, StretchProperty); + AffectsRender(SourceProperty, StretchProperty, StretchDirectionProperty); + AffectsMeasure(SourceProperty, StretchProperty, StretchDirectionProperty); } /// diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 781c93bcbe..b65f4b31d8 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -1,40 +1,50 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; namespace Avalonia.Controls { /// - /// Viewbox is used to scale single child. + /// Viewbox is used to scale single child to fit in the available space. /// /// public class Viewbox : Decorator { /// - /// The stretch property + /// Defines the property. /// public static readonly AvaloniaProperty StretchProperty = - AvaloniaProperty.RegisterDirect(nameof(Stretch), - v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform); + AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); + + /// + /// Defines the property. + /// + public static readonly StyledProperty StretchDirectionProperty = + AvaloniaProperty.Register(nameof(StretchDirection), StretchDirection.Both); private Stretch _stretch = Stretch.Uniform; + static Viewbox() + { + ClipToBoundsProperty.OverrideDefaultValue(true); + AffectsMeasure(StretchProperty, StretchDirectionProperty); + } + /// /// Gets or sets the stretch mode, /// which determines how child fits into the available space. /// - /// - /// The stretch. - /// public Stretch Stretch { get => _stretch; set => SetAndRaise(StretchProperty, ref _stretch, value); } - static Viewbox() + /// + /// Gets or sets a value controlling in what direction contents will be stretched. + /// + public StretchDirection StretchDirection { - ClipToBoundsProperty.OverrideDefaultValue(true); - AffectsMeasure(StretchProperty); + get => GetValue(StretchDirectionProperty); + set => SetValue(StretchDirectionProperty, value); } protected override Size MeasureOverride(Size availableSize) @@ -47,9 +57,9 @@ namespace Avalonia.Controls var childSize = child.DesiredSize; - var scale = GetScale(availableSize, childSize, Stretch); + var size = Stretch.CalculateSize(availableSize, childSize, StretchDirection); - return (childSize * scale).Constrain(availableSize); + return size.Constrain(availableSize); } return new Size(); @@ -62,7 +72,9 @@ namespace Avalonia.Controls if (child != null) { var childSize = child.DesiredSize; - var scale = GetScale(finalSize, childSize, Stretch); + var scale = Stretch.CalculateScaling(finalSize, childSize, StretchDirection); + + // TODO: Viewbox should have another decorator as a child so we won't affect other render transforms. var scaleTransform = child.RenderTransform as ScaleTransform; if (scaleTransform == null) @@ -81,44 +93,5 @@ namespace Avalonia.Controls return new Size(); } - - private static Vector GetScale(Size availableSize, Size childSize, Stretch stretch) - { - double scaleX = 1.0; - double scaleY = 1.0; - - bool validWidth = !double.IsPositiveInfinity(availableSize.Width); - bool validHeight = !double.IsPositiveInfinity(availableSize.Height); - - if (stretch != Stretch.None && (validWidth || validHeight)) - { - scaleX = childSize.Width <= 0.0 ? 0.0 : availableSize.Width / childSize.Width; - scaleY = childSize.Height <= 0.0 ? 0.0 : availableSize.Height / childSize.Height; - - if (!validWidth) - { - scaleX = scaleY; - } - else if (!validHeight) - { - scaleY = scaleX; - } - else - { - switch (stretch) - { - case Stretch.Uniform: - scaleX = scaleY = Math.Min(scaleX, scaleY); - break; - - case Stretch.UniformToFill: - scaleX = scaleY = Math.Max(scaleX, scaleY); - break; - } - } - } - - return new Vector(scaleX, scaleY); - } } } diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index e005bafbf9..7eaec35506 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -114,5 +114,61 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(2.0, scaleTransform.ScaleX); Assert.Equal(2.0, scaleTransform.ScaleY); } + + [Theory] + [InlineData(50, 100, 50, 100, 50, 100, 1)] + [InlineData(50, 100, 150, 150, 50, 100, 1)] + [InlineData(50, 100, 25, 50, 25, 50, 0.5)] + public void Viewbox_Should_Return_Correct_SizeAndScale_StretchDirection_DownOnly( + double childWidth, double childHeight, + double viewboxWidth, double viewboxHeight, + double expectedWidth, double expectedHeight, + double expectedScale) + { + var target = new Viewbox + { + Child = new Control { Width = childWidth, Height = childHeight }, + StretchDirection = StretchDirection.DownOnly + }; + + target.Measure(new Size(viewboxWidth, viewboxHeight)); + target.Arrange(new Rect(default, target.DesiredSize)); + + Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize); + + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(expectedScale, scaleTransform.ScaleX); + Assert.Equal(expectedScale, scaleTransform.ScaleY); + } + + [Theory] + [InlineData(50, 100, 50, 100, 50, 100, 1)] + [InlineData(50, 100, 25, 50, 25, 50, 1)] + [InlineData(50, 100, 150, 150, 75, 150, 1.5)] + public void Viewbox_Should_Return_Correct_SizeAndScale_StretchDirection_UpOnly( + double childWidth, double childHeight, + double viewboxWidth, double viewboxHeight, + double expectedWidth, double expectedHeight, + double expectedScale) + { + var target = new Viewbox + { + Child = new Control { Width = childWidth, Height = childHeight }, + StretchDirection = StretchDirection.UpOnly + }; + + target.Measure(new Size(viewboxWidth, viewboxHeight)); + target.Arrange(new Rect(default, target.DesiredSize)); + + Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize); + + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(expectedScale, scaleTransform.ScaleX); + Assert.Equal(expectedScale, scaleTransform.ScaleY); + } } } From 6e2384deb804da5cf3d453933f88ba6bce0cc126 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 13 May 2021 14:57:36 +0200 Subject: [PATCH 26/27] Use an ellipse instead. --- samples/ControlCatalog/Pages/ViewboxPage.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index f8b4a62290..f9e9c983d5 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -34,7 +34,7 @@ - + From f775f9d3f0af3eb7fa3aaa6be43659c7783767b0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 13 May 2021 16:00:14 +0200 Subject: [PATCH 27/27] Remove old viewbox samples. --- samples/ControlCatalog/Pages/ViewboxPage.xaml | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index f9e9c983d5..6e649b39a5 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -1,26 +1,6 @@ - - - F1 M 16.6309,18.6563C 17.1309, - 8.15625 29.8809,14.1563 29.8809, - 14.1563C 30.8809,11.1563 34.1308, - 11.4063 34.1308,11.4063C 33.5,12 - 34.6309,13.1563 34.6309,13.1563C - 32.1309,13.1562 31.1309,14.9062 - 31.1309,14.9062C 41.1309,23.9062 - 32.6309,27.9063 32.6309,27.9062C - 24.6309,24.9063 21.1309,22.1562 - 16.6309,18.6563 Z M 16.6309,19.9063C - 21.6309,24.1563 25.1309,26.1562 - 31.6309,28.6562C 31.6309,28.6562 - 26.3809,39.1562 18.3809,36.1563C - 18.3809,36.1563 18,38 16.3809,36.9063C - 15,36 16.3809,34.9063 16.3809,34.9063C - 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z - - @@ -51,40 +31,5 @@ - - None - Fill - Uniform - UniformToFill - - - Hello World! - - - Hello World! - - - Hello World! - - - Hello World! - - - - - - - - - - - - - - -