From 9f134f5941acd76364de057399f0c7cd1955c703 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 16:29:29 +0100 Subject: [PATCH 01/14] Bump Microsoft.Web.WebView2 from 1.0.1210.30 to 1.0.1210.39 (#6991) Bumps Microsoft.Web.WebView2 from 1.0.1210.30 to 1.0.1210.39. --- updated-dependencies: - dependency-name: Microsoft.Web.WebView2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index f298001f56e8..1507382772a2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -38,7 +38,7 @@ 3.3.3 6.0.400-preview.1.1367 4.5.0 - <_MicrosoftWebWebView2Version>1.0.1210.30 + <_MicrosoftWebWebView2Version>1.0.1210.39 <_XamarinAndroidGlideVersion>4.12.0.8 From 76a532bf3ec29326c61706fd7ffdcbe04ce2f290 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Mon, 16 May 2022 15:23:31 -0600 Subject: [PATCH 02/14] Alowl AbsoluteLayout children to auto size when height/width are not proportional (#7183) Fixes #6500 --- src/Core/src/Layouts/AbsoluteLayoutManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/src/Layouts/AbsoluteLayoutManager.cs b/src/Core/src/Layouts/AbsoluteLayoutManager.cs index d5dd7fe8d5e5..2608f279dc28 100644 --- a/src/Core/src/Layouts/AbsoluteLayoutManager.cs +++ b/src/Core/src/Layouts/AbsoluteLayoutManager.cs @@ -132,8 +132,8 @@ static double ResolveChildMeasureConstraint(double boundsValue, bool proportiona { if (boundsValue < 0) { - // If the child view doesn't have bounds set by the AbsoluteLayout, then we'll measure using the full constraint value - return constraint; + // If the child view doesn't have bounds set by the AbsoluteLayout, then we'll let it auto-size + return double.PositiveInfinity; } if (proportional) From fe7362a61a9796cab168495fb292c5756a05172e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 May 2022 14:12:07 +0100 Subject: [PATCH 03/14] Bump Microsoft.CodeAnalysis.CSharp from 4.1.0 to 4.2.0 (#7223) Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/dotnet/roslyn/releases) - [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md) - [Commits](https://github.com/dotnet/roslyn/compare/v4.1.0...Visual-Studio-2019-Version-16.0-Preview-4.2) --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.CSharp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Controls/src/SourceGen/Controls.SourceGen.csproj | 2 +- .../TestUtils.DeviceTests.Runners.SourceGen.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controls/src/SourceGen/Controls.SourceGen.csproj b/src/Controls/src/SourceGen/Controls.SourceGen.csproj index 25604ec1fcfa..d36290f53ac5 100644 --- a/src/Controls/src/SourceGen/Controls.SourceGen.csproj +++ b/src/Controls/src/SourceGen/Controls.SourceGen.csproj @@ -36,7 +36,7 @@ - + diff --git a/src/TestUtils/src/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj b/src/TestUtils/src/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj index 4e39028fa1be..60b755fcad74 100644 --- a/src/TestUtils/src/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj +++ b/src/TestUtils/src/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj @@ -9,7 +9,7 @@ - + From 820c03a59d44657e9fe4aac31eef4fb48ff6aa7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 May 2022 14:12:22 +0100 Subject: [PATCH 04/14] Bump Microsoft.Net.Compilers.Toolset from 4.1.0 to 4.2.0 (#7224) Bumps [Microsoft.Net.Compilers.Toolset](https://github.com/dotnet/roslyn) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/dotnet/roslyn/releases) - [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md) - [Commits](https://github.com/dotnet/roslyn/compare/v4.1.0...Visual-Studio-2019-Version-16.0-Preview-4.2) --- updated-dependencies: - dependency-name: Microsoft.Net.Compilers.Toolset dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 580e691dc214..078ff0522111 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -99,6 +99,6 @@ - + From a6c12a13556e5111b658109ed9daf2916f3c4127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 17 May 2022 15:13:19 +0200 Subject: [PATCH 05/14] Added pending lifecycle events (#7225) --- .../{Startup.cs => MauiProgram.cs} | 0 .../samples/Controls.Sample/MauiWindow.cs | 35 +++++++++++++++++++ .../samples/Controls.Sample/XamlApp.xaml.cs | 8 +++-- .../AppHostBuilderExtensions.Android.cs | 4 +-- .../AppHostBuilderExtensions.Windows.cs | 4 +-- .../MauiAppCompatActivity.Lifecycle.cs | 8 ++++- .../Platform/iOS/MauiUIApplicationDelegate.cs | 6 ++++ 7 files changed, 55 insertions(+), 10 deletions(-) rename src/Controls/samples/Controls.Sample/{Startup.cs => MauiProgram.cs} (100%) create mode 100644 src/Controls/samples/Controls.Sample/MauiWindow.cs diff --git a/src/Controls/samples/Controls.Sample/Startup.cs b/src/Controls/samples/Controls.Sample/MauiProgram.cs similarity index 100% rename from src/Controls/samples/Controls.Sample/Startup.cs rename to src/Controls/samples/Controls.Sample/MauiProgram.cs diff --git a/src/Controls/samples/Controls.Sample/MauiWindow.cs b/src/Controls/samples/Controls.Sample/MauiWindow.cs new file mode 100644 index 000000000000..12bbdba3d0ee --- /dev/null +++ b/src/Controls/samples/Controls.Sample/MauiWindow.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.Maui.Controls; + +namespace Maui.Controls.Sample +{ + public class MauiWindow : Window + { + public MauiWindow() : base() { } + public MauiWindow(Page page) : base(page) { } + + protected override void OnCreated() + { + Console.WriteLine("OnCreated"); + base.OnCreated(); + } + + protected override void OnStopped() + { + Console.WriteLine("OnStopped"); + base.OnStopped(); + } + + protected override void OnResumed() + { + Console.WriteLine("OnResumed"); + base.OnResumed(); + } + + protected override void OnDestroying() + { + Console.WriteLine("OnDestroying"); + base.OnDestroying(); + } + } +} \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample/XamlApp.xaml.cs b/src/Controls/samples/Controls.Sample/XamlApp.xaml.cs index 52f6cd5aa59d..efae212f4cfc 100644 --- a/src/Controls/samples/Controls.Sample/XamlApp.xaml.cs +++ b/src/Controls/samples/Controls.Sample/XamlApp.xaml.cs @@ -5,7 +5,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui; using Microsoft.Maui.Controls; -using Microsoft.Maui.Devices; using Microsoft.Maui.Storage; namespace Maui.Controls.Sample @@ -49,8 +48,11 @@ async void LoadAsset() // Must not use MainPage for multi-window protected override Window CreateWindow(IActivationState activationState) { - var window = new Window(Services.GetRequiredService()); - window.Title = ".NET MAUI Samples Gallery"; + var window = new MauiWindow(Services.GetRequiredService()) + { + Title = ".NET MAUI Samples Gallery" + }; + return window; } diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs index c02faea6b682..1c5bbde2c446 100644 --- a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs @@ -1,6 +1,4 @@ -using System; using Microsoft.Maui.Hosting; -using Microsoft.Maui.LifecycleEvents; namespace Microsoft.Maui.LifecycleEvents { @@ -66,4 +64,4 @@ static void OnConfigureLifeCycle(IAndroidLifecycleBuilder android) }); } } -} +} \ No newline at end of file diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs index 56a51980422e..616a100c3977 100644 --- a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs @@ -1,9 +1,7 @@ using Microsoft.Maui.Hosting; -using Microsoft.Maui.LifecycleEvents; -using System; namespace Microsoft.Maui.LifecycleEvents -{ +{ public static partial class AppHostBuilderExtensions { internal static MauiAppBuilder ConfigureCrossPlatformLifecycleEvents(this MauiAppBuilder builder) => diff --git a/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs b/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs index d441f89d459c..8a2ff27561db 100644 --- a/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs +++ b/src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs @@ -3,7 +3,6 @@ using Android.Content.PM; using Android.Content.Res; using Android.OS; -using Android.Views; using Microsoft.Maui.Devices; using Microsoft.Maui.LifecycleEvents; @@ -95,5 +94,12 @@ protected override void OnRestoreInstanceState(Bundle savedInstanceState) MauiApplication.Current?.Services?.InvokeLifecycleEvents(del => del(this, savedInstanceState)); } + + protected override void OnDestroy() + { + base.OnDestroy(); + + MauiApplication.Current?.Services?.InvokeLifecycleEvents(del => del(this)); + } } } \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs index d66d247979c0..6f379ef97604 100644 --- a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs @@ -114,6 +114,12 @@ public virtual void OnResignActivation(UIApplication application) Services?.InvokeLifecycleEvents(del => del(application)); } + [Export("sceneDidDisconnect:")] + public void DidDisconnect(UIScene scene) + { + Services?.InvokeLifecycleEvents(del => del(scene)); + } + [Export("applicationWillTerminate:")] public virtual void WillTerminate(UIApplication application) { From a51243a75bebc5571e6351e0e93cf6b40a592877 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Tue, 17 May 2022 16:46:50 +0200 Subject: [PATCH 06/14] Fix Android FormattedText and related platforms (#7219) * Reduce line length so we can work * Set initial Span.FontSize => double.NaN Fixes #6801 This initial value of Nan (or could be 0) indicates to the layout engine that the size must come from the Label. If we set it here, then there is no way of knowing that the size was not actually set so uses the size. If the size is Nan, it falls back to the "default font size" which is not really default but actually the font size of the label. * Correctly apply span values to spans Fixes #7220 All: - set the default parameter values for line height to be -1 as that is what is the default currently Android: - don't capture the TextView.Paint as that is always wrong initially since none of the other properties are set - don't fall back to the Label.LineHeight to the spans as that is always applied - regardless of span values - pass the Label.CharacterSpacing down to be consistent - pass the Label.TextDecorations down as well - for text decorations, use the platform spans - split a LetterSpacingSpan out of the FontSpan so that they can be individually applied * Update src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs Co-authored-by: campersau --- src/Controls/src/Core/FontExtensions.cs | 2 +- .../Extensions/FormattedStringExtensions.cs | 151 +++++++++--------- .../Extensions/FormattedStringExtensions.cs | 42 ++++- .../Extensions/FormattedStringExtensions.cs | 24 ++- src/Controls/src/Core/Span.cs | 2 +- .../DeviceTests/Elements/Label/LabelTests.cs | 105 +++++++++++- src/Core/src/Fonts/FontManager.Android.cs | 4 +- src/Core/src/Fonts/FontManager.Tizen.cs | 4 +- src/Core/src/Fonts/FontManager.Windows.cs | 2 +- src/Core/src/Fonts/FontManager.iOS.cs | 2 +- src/Core/src/Primitives/Font.cs | 2 +- 11 files changed, 239 insertions(+), 101 deletions(-) diff --git a/src/Controls/src/Core/FontExtensions.cs b/src/Controls/src/Core/FontExtensions.cs index 895daac4b60a..d3ba12a720e3 100644 --- a/src/Controls/src/Core/FontExtensions.cs +++ b/src/Controls/src/Core/FontExtensions.cs @@ -30,7 +30,7 @@ public static FontAttributes GetFontAttributes(this Font font) public static Font ToFont(this IFontElement element, double? defaultSize = null) { var size = element.FontSize; - if (defaultSize.HasValue && (size == 0 || size == double.NaN)) + if (defaultSize.HasValue && (size <= 0 || double.IsNaN(size))) size = defaultSize.Value; return Font.OfSize(element.FontFamily, size, enableScaling: element.FontAutoScalingEnabled).WithAttributes(element.FontAttributes); diff --git a/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs index 1d1cc8136699..01c8a1f3dbd9 100644 --- a/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs +++ b/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs @@ -15,9 +15,27 @@ namespace Microsoft.Maui.Controls.Platform public static class FormattedStringExtensions { public static SpannableString ToSpannableString(this Label label) - => ToSpannableString(label.FormattedText, label.RequireFontManager(), (label.Handler?.PlatformView as TextView)?.Paint, label.Handler?.MauiContext?.Context, label.LineHeight, label.HorizontalTextAlignment, label.ToFont(), label.TextColor, label.TextTransform); - - public static SpannableString ToSpannableString(this FormattedString formattedString, IFontManager fontManager, TextPaint? textPaint = null, Context? context = null, double defaultLineHeight = 0d, TextAlignment defaultHorizontalAlignment = TextAlignment.Start, Font? defaultFont = null, Graphics.Color? defaultColor = null, TextTransform defaultTextTransform = TextTransform.Default) + => ToSpannableString( + label.FormattedText, + label.RequireFontManager(), + label.Handler?.MauiContext?.Context, + label.CharacterSpacing, + label.HorizontalTextAlignment, + label.ToFont(), + label.TextColor, + label.TextTransform, + label.TextDecorations); + + public static SpannableString ToSpannableString( + this FormattedString formattedString, + IFontManager fontManager, + Context? context = null, + double defaultCharacterSpacing = 0d, + TextAlignment defaultHorizontalAlignment = TextAlignment.Start, + Font? defaultFont = null, + Graphics.Color? defaultColor = null, + TextTransform defaultTextTransform = TextTransform.Default, + TextDecorations defaultTextDecorations = TextDecorations.None) { if (formattedString == null) return new SpannableString(string.Empty); @@ -53,46 +71,43 @@ public static SpannableString ToSpannableString(this FormattedString formattedSt int end = start + text.Length; c = end; + // TextColor var textColor = span.TextColor ?? defaultColor; - - if (textColor != null) - { + if (textColor is not null) spannable.SetSpan(new ForegroundColorSpan(textColor.ToPlatform()), start, end, SpanTypes.InclusiveExclusive); - } - if (span.BackgroundColor != null) - { + // BackgroundColor + if (span.BackgroundColor is not null) spannable.SetSpan(new BackgroundColorSpan(span.BackgroundColor.ToPlatform()), start, end, SpanTypes.InclusiveExclusive); - } - var lineHeight = span.LineHeight >= 0 - ? span.LineHeight - : defaultLineHeight; + // LineHeight + if (span.LineHeight >= 0) + spannable.SetSpan(new LineHeightSpan(span.LineHeight), start, end, SpanTypes.InclusiveExclusive); - if (lineHeight >= 0) - { - spannable.SetSpan(new LineHeightSpan(textPaint, lineHeight), start, end, SpanTypes.InclusiveExclusive); - } + // CharacterSpacing + var characterSpacing = span.CharacterSpacing >= 0 + ? span.CharacterSpacing + : defaultCharacterSpacing; + if (characterSpacing >= 0) + spannable.SetSpan(new LetterSpacingSpan(characterSpacing.ToEm()), start, end, SpanTypes.InclusiveInclusive); + // Font var font = span.ToFont(defaultFontSize); if (font.IsDefault && defaultFont.HasValue) font = defaultFont.Value; - if (!font.IsDefault) - { - spannable.SetSpan( - new FontSpan(font, context, span.CharacterSpacing.ToEm(), defaultHorizontalAlignment, fontManager), - start, - end, - SpanTypes.InclusiveInclusive); - } - - if (span.IsSet(Span.TextDecorationsProperty)) - { - spannable.SetSpan(new TextDecorationSpan(span), start, end, SpanTypes.InclusiveInclusive); - } - + spannable.SetSpan(new FontSpan(font, fontManager, context), start, end, SpanTypes.InclusiveInclusive); + + // TextDecorations + var textDecorations = span.IsSet(Span.TextDecorationsProperty) + ? span.TextDecorations + : defaultTextDecorations; + if (textDecorations.HasFlag(TextDecorations.Strikethrough)) + spannable.SetSpan(new StrikethroughSpan(), start, end, SpanTypes.InclusiveInclusive); + if (textDecorations.HasFlag(TextDecorations.Underline)) + spannable.SetSpan(new UnderlineSpan(), start, end, SpanTypes.InclusiveInclusive); } + return spannable; } @@ -118,7 +133,6 @@ public static void RecalculateSpanPositions(this TextView textView, Label elemen int count = 0; IList totalLineHeights = new List(); -#pragma warning disable CA1416 // https://github.com/xamarin/xamarin-android/issues/6962 for (int i = 0; i < spannableString.Length(); i = next) { var type = Java.Lang.Class.FromType(typeof(Java.Lang.Object)); @@ -136,7 +150,7 @@ public static void RecalculateSpanPositions(this TextView textView, Label elemen // Get all spans in the range - Android can have overlapping spans var spans = spannableString.GetSpans(i, next, type); - if (spans == null) + if (spans is null || spans.Length == 0) continue; var startSpan = spans[0]; @@ -179,26 +193,21 @@ public static void RecalculateSpanPositions(this TextView textView, Label elemen ((ISpatialElement)span).Region = Region.FromLines(lineHeights, labelWidth, startX, endX, yaxis).Inflate(10); } -#pragma warning restore CA1416 // 'SpannableString.Length()' is only supported on: 'android' 29.0 and later } class FontSpan : MetricAffectingSpan { - public FontSpan(Font font, Context? context, float characterSpacing, TextAlignment? horizontalTextAlignment, IFontManager fontManager) + readonly Font _font; + readonly IFontManager _fontManager; + readonly Context? _context; + + public FontSpan(Font font, IFontManager fontManager, Context? context) { - Font = font; - Context = context; - CharacterSpacing = characterSpacing; - FontManager = fontManager; - HorizontalTextAlignment = horizontalTextAlignment; + _font = font; + _fontManager = fontManager; + _context = context; } - public readonly IFontManager FontManager; - public readonly Font Font; - public readonly Context? Context; - public readonly float CharacterSpacing; - public readonly TextAlignment? HorizontalTextAlignment; - public override void UpdateDrawState(TextPaint? tp) { if (tp != null) @@ -210,28 +219,26 @@ public override void UpdateMeasureState(TextPaint p) Apply(p); } - void Apply(Paint paint) + void Apply(TextPaint paint) { - paint.SetTypeface(Font.ToTypeface(FontManager)); - float value = (float)Font.Size; + paint.SetTypeface(_font.ToTypeface(_fontManager)); paint.TextSize = TypedValue.ApplyDimension( - Font.AutoScalingEnabled ? ComplexUnitType.Sp : ComplexUnitType.Dip, - value, Context?.Resources?.DisplayMetrics ?? AAplication.Context.Resources!.DisplayMetrics); - - paint.LetterSpacing = CharacterSpacing; + _font.AutoScalingEnabled ? ComplexUnitType.Sp : ComplexUnitType.Dip, + (float)_font.Size, + (_context ?? AAplication.Context)?.Resources?.DisplayMetrics); } } - class TextDecorationSpan : MetricAffectingSpan + class LetterSpacingSpan : MetricAffectingSpan { - public TextDecorationSpan(Span span) + readonly float _letterSpacing; + + public LetterSpacingSpan(double letterSpacing) { - Span = span; + _letterSpacing = (float)letterSpacing; } - public Span Span { get; } - public override void UpdateDrawState(TextPaint? tp) { if (tp != null) @@ -243,35 +250,27 @@ public override void UpdateMeasureState(TextPaint p) Apply(p); } - void Apply(Paint paint) + void Apply(TextPaint paint) { - var textDecorations = Span.TextDecorations; - paint.UnderlineText = (textDecorations & TextDecorations.Underline) != 0; - paint.StrikeThruText = (textDecorations & TextDecorations.Strikethrough) != 0; + paint.LetterSpacing = _letterSpacing; } } class LineHeightSpan : Java.Lang.Object, ILineHeightSpan { - private double _lineHeight; - private int _ascent; - private int _descent; + readonly double _relativeLineHeight; - public LineHeightSpan(TextPaint? paint, double lineHeight) + public LineHeightSpan(double relativeLineHeight) { - _lineHeight = lineHeight; - var fm = paint?.GetFontMetricsInt(); - _ascent = fm?.Ascent ?? 1; - _descent = fm?.Descent ?? 1; + _relativeLineHeight = relativeLineHeight; } - public void ChooseHeight(Java.Lang.ICharSequence? text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt? fm) + public void ChooseHeight(Java.Lang.ICharSequence? text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt? fm) { - if (fm != null) - { - fm.Ascent = (int)(_ascent * _lineHeight); - fm.Descent = (int)(_descent * _lineHeight); - } + if (fm is null) + return; + + fm.Ascent = (int)(fm.Top * _relativeLineHeight); } } } diff --git a/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs index 1903a845086d..477acd465038 100644 --- a/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs +++ b/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs @@ -11,9 +11,25 @@ namespace Microsoft.Maui.Controls.Platform public static class FormattedStringExtensions { public static void UpdateInlines(this TextBlock textBlock, Label label) - => UpdateInlines(textBlock, label.RequireFontManager(false), label.FormattedText, label.LineHeight, label.HorizontalTextAlignment, label.ToFont(), label.TextColor, label.TextTransform); - - public static void UpdateInlines(this TextBlock textBlock, IFontManager fontManager, FormattedString formattedString, double defaultLineHeight = 0d, TextAlignment defaultHorizontalAlignment = TextAlignment.Start, Font? defaultFont = null, Color? defaultColor = null, TextTransform defaultTextTransform = TextTransform.Default) + => UpdateInlines( + textBlock, + label.RequireFontManager(false), + label.FormattedText, + label.LineHeight, + label.HorizontalTextAlignment, + label.ToFont(), + label.TextColor, + label.TextTransform); + + public static void UpdateInlines( + this TextBlock textBlock, + IFontManager fontManager, + FormattedString formattedString, + double defaultLineHeight = -1d, + TextAlignment defaultHorizontalAlignment = TextAlignment.Start, + Font? defaultFont = null, + Color? defaultColor = null, + TextTransform defaultTextTransform = TextTransform.Default) { textBlock.Inlines.Clear(); // Have to implement a measure here, otherwise inline.ContentStart and ContentEnd will be null, when used in RecalculatePositions @@ -57,7 +73,14 @@ public static void UpdateInlines(this TextBlock textBlock, IFontManager fontMana } } - public static IEnumerable> ToRunAndColorsTuples(this FormattedString formattedString, IFontManager fontManager, double defaultLineHeight = 0d, TextAlignment defaultHorizontalAlignment = TextAlignment.Start, Font? defaultFont = null, Color? defaultColor = null, TextTransform defaultTextTransform = TextTransform.Default) + public static IEnumerable> ToRunAndColorsTuples( + this FormattedString formattedString, + IFontManager fontManager, + double defaultLineHeight = -1d, + TextAlignment defaultHorizontalAlignment = TextAlignment.Start, + Font? defaultFont = null, + Color? defaultColor = null, + TextTransform defaultTextTransform = TextTransform.Default) { var runs = new List>(); @@ -74,15 +97,22 @@ public static void UpdateInlines(this TextBlock textBlock, IFontManager fontMana return runs; } - public static Tuple ToRunAndColorsTuple(this Span span, IFontManager fontManager, Font? defaultFont = null, Color? defaultColor = null, TextTransform defaultTextTransform = TextTransform.Default) + public static Tuple ToRunAndColorsTuple( + this Span span, + IFontManager fontManager, + Font? defaultFont = null, + Color? defaultColor = null, + TextTransform defaultTextTransform = TextTransform.Default) { + var defaultFontSize = defaultFont?.Size ?? fontManager.DefaultFontSize; + var transform = span.TextTransform != TextTransform.Default ? span.TextTransform : defaultTextTransform; var text = TextTransformUtilites.GetTransformedText(span.Text, transform); var run = new Run { Text = text ?? string.Empty }; - var font = span.ToFont(); + var font = span.ToFont(defaultFontSize); if (font.IsDefault && defaultFont.HasValue) font = defaultFont.Value; diff --git a/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs index b35873a1c27f..29232590f552 100644 --- a/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs +++ b/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs @@ -26,8 +26,14 @@ public static class FormattedStringExtensions label.TextColor, label.TextTransform); - - public static NSAttributedString ToNSAttributedString(this FormattedString formattedString, IFontManager fontManager, double defaultLineHeight = 0d, TextAlignment defaultHorizontalAlignment = TextAlignment.Start, Font? defaultFont = null, Color? defaultColor = null, TextTransform defaultTextTransform = TextTransform.Default) + public static NSAttributedString ToNSAttributedString( + this FormattedString formattedString, + IFontManager fontManager, + double defaultLineHeight = -1d, + TextAlignment defaultHorizontalAlignment = TextAlignment.Start, + Font? defaultFont = null, + Color? defaultColor = null, + TextTransform defaultTextTransform = TextTransform.Default) { if (formattedString == null) return new NSAttributedString(string.Empty); @@ -45,8 +51,17 @@ public static NSAttributedString ToNSAttributedString(this FormattedString forma return attributed; } - public static NSAttributedString ToNSAttributedString(this Span span, IFontManager fontManager, double defaultLineHeight = 0d, TextAlignment defaultHorizontalAlignment = TextAlignment.Start, Font? defaultFont = null, Color? defaultColor = null, TextTransform defaultTextTransform = TextTransform.Default) + public static NSAttributedString ToNSAttributedString( + this Span span, + IFontManager fontManager, + double defaultLineHeight = -1d, + TextAlignment defaultHorizontalAlignment = TextAlignment.Start, + Font? defaultFont = null, + Color? defaultColor = null, + TextTransform defaultTextTransform = TextTransform.Default) { + var defaultFontSize = defaultFont?.Size ?? fontManager.DefaultFontSize; + var transform = span.TextTransform != TextTransform.Default ? span.TextTransform : defaultTextTransform; var text = TextTransformUtilites.GetTransformedText(span.Text, transform); @@ -71,7 +86,7 @@ public static NSAttributedString ToNSAttributedString(this Span span, IFontManag _ => UITextAlignment.Left }; - var font = span.ToFont(); + var font = span.ToFont(defaultFontSize); if (font.IsDefault && defaultFont.HasValue) font = defaultFont.Value; @@ -110,6 +125,5 @@ public static NSAttributedString ToNSAttributedString(this Span span, IFontManag return attrString; } - } } \ No newline at end of file diff --git a/src/Controls/src/Core/Span.cs b/src/Controls/src/Core/Span.cs index 9542e3343de5..423cc531cc76 100644 --- a/src/Controls/src/Core/Span.cs +++ b/src/Controls/src/Core/Span.cs @@ -159,7 +159,7 @@ void IFontElement.OnFontSizeChanged(double oldValue, double newValue) } double IFontElement.FontSizeDefaultValueCreator() => - this.GetDefaultFontSize(); + double.NaN; void IFontElement.OnFontAttributesChanged(FontAttributes oldValue, FontAttributes newValue) { diff --git a/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.cs b/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.cs index 04b9db7b3472..4e9783734c9b 100644 --- a/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.cs @@ -225,11 +225,7 @@ public async Task NegativeMaxValueWithWrapIsCorrect(int expectedLines) Assert.Equal(expectedLines, platformValue); } - [Theory( -#if IOS - Skip = "Not able to debug iOS right now" -#endif - )] + [Theory] [InlineData(TextAlignment.Center)] [InlineData(TextAlignment.Start)] [InlineData(TextAlignment.End)] @@ -269,5 +265,102 @@ public async Task FormattedStringSpanTextHasCorrectLayoutWhenAligned(TextAlignme await normalBitmap.AssertEqual(formattedBitmap); }); } + + [Theory( +#if IOS + Skip = "iOS has issues with null graphics contexts." +#endif + )] + [InlineData(10)] + [InlineData(20)] + [InlineData(30)] + public async Task UpdatingFormattedTextResultsINTheSmaeLayout(double fontSize) + { + var initialLabel = new Label + { + WidthRequest = 200, + HeightRequest = 60, + FontSize = fontSize, + FormattedText = GetFormattedString(), + }; + + var updatedLabel = new Label + { + WidthRequest = 200, + HeightRequest = 60, + FontSize = fontSize, + }; + + await InvokeOnMainThreadAsync(async () => + { + var initialHandler = CreateHandler(initialLabel); + var initialBitmap = await initialHandler.PlatformView.ToBitmap(); + + var updatedHandler = CreateHandler(updatedLabel); + + updatedLabel.FormattedText = GetFormattedString(); + + var updatedBitmap = await updatedHandler.PlatformView.ToBitmap(); + + await updatedBitmap.AssertEqual(initialBitmap); + }); + + static FormattedString GetFormattedString() => + new FormattedString + { + Spans = + { + new Span { Text = "first" }, + new Span { Text = "\n"}, + new Span { Text = "second"}, + } + }; + } + + [Theory( +#if ANDROID + Skip = "Android does not have the exact same layout with a string vs spans." +#endif + )] + [InlineData(10)] + [InlineData(20)] + [InlineData(30)] + public async Task InitialFormattedTextMatchesText(double fontSize) + { + var formattedLabel = new Label + { + WidthRequest = 200, + HeightRequest = 60, + FontSize = fontSize, + FormattedText = new FormattedString + { + Spans = + { + new Span { Text = "first" }, + new Span { Text = "\n"}, + new Span { Text = "second"}, + } + }, + }; + + var normalLabel = new Label + { + WidthRequest = 200, + HeightRequest = 60, + FontSize = fontSize, + Text = "first\nsecond" + }; + + await InvokeOnMainThreadAsync(async () => + { + var formattedHandler = CreateHandler(formattedLabel); + var formattedBitmap = await formattedHandler.PlatformView.ToBitmap(); + + var normalHandler = CreateHandler(normalLabel); + var normalBitmap = await normalHandler.PlatformView.ToBitmap(); + + await normalBitmap.AssertEqual(formattedBitmap); + }); + } } -} \ No newline at end of file +} diff --git a/src/Core/src/Fonts/FontManager.Android.cs b/src/Core/src/Fonts/FontManager.Android.cs index 6365bfa35c70..a8a73c759e98 100644 --- a/src/Core/src/Fonts/FontManager.Android.cs +++ b/src/Core/src/Fonts/FontManager.Android.cs @@ -42,8 +42,8 @@ public FontManager(IFontRegistrar fontRegistrar, IServiceProvider? serviceProvid public FontSize GetFontSize(Font font, float defaultFontSize = 0) { - var size = font.Size <= 0 - ? (defaultFontSize > 0 ? defaultFontSize : 14f) + var size = font.Size <= 0 || double.IsNaN(font.Size) + ? (defaultFontSize > 0 ? defaultFontSize : (float)DefaultFontSize) : (float)font.Size; ComplexUnitType units; diff --git a/src/Core/src/Fonts/FontManager.Tizen.cs b/src/Core/src/Fonts/FontManager.Tizen.cs index 83b070ac572c..f256d1c909a3 100644 --- a/src/Core/src/Fonts/FontManager.Tizen.cs +++ b/src/Core/src/Fonts/FontManager.Tizen.cs @@ -22,7 +22,9 @@ public FontManager(IFontRegistrar fontRegistrar, IServiceProvider? serviceProvid public string GetFont(Font font) { - var size = (float)font.Size; + var size = font.Size <= 0 || double.IsNaN(font.Size) + ? (float)DefaultFontSize + : (float)font.Size; return GetFont(font.Family, size, font.Slant, GetNativeFontFamily); } diff --git a/src/Core/src/Fonts/FontManager.Windows.cs b/src/Core/src/Fonts/FontManager.Windows.cs index 62f82c58b8b9..c0d8612e8c7c 100644 --- a/src/Core/src/Fonts/FontManager.Windows.cs +++ b/src/Core/src/Fonts/FontManager.Windows.cs @@ -46,7 +46,7 @@ public FontFamily GetFontFamily(Font font) } public double GetFontSize(Font font, double defaultFontSize = 0) => - font.Size <= 0 + font.Size <= 0 || double.IsNaN(font.Size) ? (defaultFontSize > 0 ? defaultFontSize : DefaultFontSize) : font.Size; diff --git a/src/Core/src/Fonts/FontManager.iOS.cs b/src/Core/src/Fonts/FontManager.iOS.cs index 4751352ef5ff..13d5d470dd9c 100644 --- a/src/Core/src/Fonts/FontManager.iOS.cs +++ b/src/Core/src/Fonts/FontManager.iOS.cs @@ -46,7 +46,7 @@ public FontManager(IFontRegistrar fontRegistrar, IServiceProvider? serviceProvid GetFont(font, defaultFontSize, CreateFont); double GetFontSize(Font font, double defaultFontSize = 0) => - font.Size <= 0 + font.Size <= 0 || double.IsNaN(font.Size) ? (defaultFontSize > 0 ? (float)defaultFontSize : DefaultFont.PointSize) : (nfloat)font.Size; diff --git a/src/Core/src/Primitives/Font.cs b/src/Core/src/Primitives/Font.cs index 11a1a182c73e..5bac71b2929f 100644 --- a/src/Core/src/Primitives/Font.cs +++ b/src/Core/src/Primitives/Font.cs @@ -16,7 +16,7 @@ namespace Microsoft.Maui public FontSlant Slant { get; } /// - public bool IsDefault => Family == null && (Size == 0 || Size == double.NaN) && Slant == FontSlant.Default && Weight == FontWeight.Regular; + public bool IsDefault => Family == null && (Size <= 0 || double.IsNaN(Size)) && Slant == FontSlant.Default && Weight == FontWeight.Regular; static Font _default = default(Font).WithWeight(FontWeight.Regular); /// From f1afbd28524b2e5c1bfb50393b3b9608ecee5b56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 May 2022 16:14:17 +0100 Subject: [PATCH 07/14] Bump Castle.Core from 4.4.1 to 5.0.0 (#7256) Bumps [Castle.Core](https://github.com/castleproject/Core) from 4.4.1 to 5.0.0. - [Release notes](https://github.com/castleproject/Core/releases) - [Changelog](https://github.com/castleproject/Core/blob/master/CHANGELOG.md) - [Commits](https://github.com/castleproject/Core/compare/v4.4.1...v5.0.0) --- updated-dependencies: - dependency-name: Castle.Core dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../ControlGallery/test/WinUI.UITests/WinUI.UITests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compatibility/ControlGallery/test/WinUI.UITests/WinUI.UITests.csproj b/src/Compatibility/ControlGallery/test/WinUI.UITests/WinUI.UITests.csproj index f850e532119f..96d74f484746 100644 --- a/src/Compatibility/ControlGallery/test/WinUI.UITests/WinUI.UITests.csproj +++ b/src/Compatibility/ControlGallery/test/WinUI.UITests/WinUI.UITests.csproj @@ -16,7 +16,7 @@ - + From c6317a8539b4755b901b664feb3e8842ed74abb8 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Tue, 17 May 2022 15:39:57 -0500 Subject: [PATCH 08/14] Add a Semaphore for AttachAndRun (#7269) --- .../src/DeviceTests/AssertionExtensions.Android.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs b/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs index a484ebe1f696..b58075ff0664 100644 --- a/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs +++ b/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs @@ -95,6 +95,10 @@ public static AColor ColorAtPoint(this Bitmap bitmap, int x, int y, bool include return true; }); + // Android doesn't handle adding and removing views in parallel very well + // If a view is removed while a different test triggers a layout then you hit + // a NRE exception + static SemaphoreSlim _attachAndRunSemaphore = new SemaphoreSlim(1); public static async Task AttachAndRun(this AView view, Func> action) { if (view.Parent is WrapperView wrapper) @@ -115,17 +119,21 @@ public static async Task AttachAndRun(this AView view, Func> actio var act = context.GetActivity()!; var rootView = act.FindViewById(Android.Resource.Id.Content)!; - layout.AddView(view); - rootView.AddView(layout); + view.Id = AView.GenerateViewId(); + layout.Id = AView.GenerateViewId(); try { + await _attachAndRunSemaphore.WaitAsync(); + layout.AddView(view); + rootView.AddView(layout); return await Run(view, action); } finally { rootView.RemoveView(layout); layout.RemoveView(view); + _attachAndRunSemaphore.Release(); } } else From ad686472c90fbf3b4b78e6414443a1038a5002a1 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Tue, 17 May 2022 17:04:15 -0600 Subject: [PATCH 09/14] Allow LayoutView to pass touches to child controls when CascadeInputTransparent is false (#7236) * Allow LayoutView to pass touches to child controls when CascadeInputTransparent is false Fixes #6574 * Remove tests that don't make sense anymore --- .../GestureManager/GestureManager.iOS.cs | 58 +++++-------------- .../tests/DeviceTests/GestureTests.iOS.cs | 28 --------- src/Core/src/Platform/iOS/LayoutView.cs | 32 ++++++++++ .../Handlers/HandlerTestBaseOfT.iOS.cs | 7 +++ 4 files changed, 55 insertions(+), 70 deletions(-) diff --git a/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs b/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs index 4ff0637bb9ef..e76e32c59418 100644 --- a/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs +++ b/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs @@ -373,40 +373,6 @@ static bool ShouldRecognizeTapsTogether(NativeGestureRecognizer gesture, NativeG return true; } - // This logic should all be replaced once we implement the "InputTransparent" property - // https://github.com/dotnet/maui/issues/1190 - bool? _previousUserInteractionEnabled; - void CalculateUserInteractionEnabled() - { - if (ElementGestureRecognizers == null || _platformView == null || _handler?.VirtualView == null) - return; - - bool hasGestureRecognizers = ElementGestureRecognizers.Count > 0; - - // If no gestures have ever been added then don't do anything - if (!hasGestureRecognizers && _previousUserInteractionEnabled == null) - return; - - _previousUserInteractionEnabled ??= _platformView.UserInteractionEnabled; - - if (hasGestureRecognizers) - { - _platformView.UserInteractionEnabled = true; - } - else - { - _platformView.UserInteractionEnabled = _previousUserInteractionEnabled.Value; - - // These are the known places where UserInteractionEnabled is modified inside Maui.Core - // Once we implement "InputTransparent" all of this should just get managed the "InputTransparent" mapper property - if (_handler.VirtualView is ITextInput) - _handler.UpdateValue(nameof(ITextInput.IsReadOnly)); - - _handler.UpdateValue(nameof(IView.IsEnabled)); - _previousUserInteractionEnabled = null; - } - } - void LoadRecognizers() { if (ElementGestureRecognizers == null) @@ -418,7 +384,6 @@ void LoadRecognizers() _shouldReceiveTouch = ShouldReceiveTouch; } - CalculateUserInteractionEnabled(); UIDragInteraction? uIDragInteraction = null; UIDropInteraction? uIDropInteraction = null; @@ -526,7 +491,20 @@ void LoadRecognizers() bool ShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch) { - if (touch.View == _handler.PlatformView) + var platformView = _handler?.PlatformView; + var virtualView = _handler?.VirtualView; + + if (virtualView == null || platformView == null) + { + return false; + } + + if (virtualView.InputTransparent) + { + return false; + } + + if (touch.View == platformView) { return true; } @@ -534,13 +512,9 @@ bool ShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch) // If the touch is coming from the UIView our handler is wrapping (e.g., if it's // wrapping a UIView which already has a gesture recognizer), then we should let it through // (This goes for children of that control as well) - if (_handler?.PlatformView == null) - { - return false; - } - if (touch.View.IsDescendantOfView(_handler.PlatformView) && - (touch.View.GestureRecognizers?.Length > 0 || _handler.PlatformView.GestureRecognizers?.Length > 0)) + if (touch.View.IsDescendantOfView(platformView) && + (touch.View.GestureRecognizers?.Length > 0 || platformView.GestureRecognizers?.Length > 0)) { return true; } diff --git a/src/Controls/tests/DeviceTests/GestureTests.iOS.cs b/src/Controls/tests/DeviceTests/GestureTests.iOS.cs index 382e1262dfa6..bf581e129d46 100644 --- a/src/Controls/tests/DeviceTests/GestureTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/GestureTests.iOS.cs @@ -27,33 +27,5 @@ public async Task UserInteractionEnabledTrueWhenInitializedWithGestureRecognizer Assert.True(handler.PlatformView.UserInteractionEnabled); }); } - - [Fact] - public async Task UserInteractionEnabledSetAfterAddingGestureRecognizer() - { - var label = new Label(); - - await InvokeOnMainThreadAsync(() => - { - var handler = CreateHandler(label); - label.GestureRecognizers.Add(new TapGestureRecognizer() { NumberOfTapsRequired = 1 }); - Assert.True(handler.PlatformView.UserInteractionEnabled); - }); - } - - [Fact] - public async Task UserInteractionEnabledUnsetAfterRemovingGestureRecognizer() - { - var label = new Label(); - label.GestureRecognizers.Add(new TapGestureRecognizer() { NumberOfTapsRequired = 1 }); - - await InvokeOnMainThreadAsync(() => - { - var handler = CreateHandler(label); - Assert.True(handler.PlatformView.UserInteractionEnabled); - label.GestureRecognizers.Clear(); - Assert.False(handler.PlatformView.UserInteractionEnabled); - }); - } } } diff --git a/src/Core/src/Platform/iOS/LayoutView.cs b/src/Core/src/Platform/iOS/LayoutView.cs index 9e62b4ec6ec6..fd6d55fc0574 100644 --- a/src/Core/src/Platform/iOS/LayoutView.cs +++ b/src/Core/src/Platform/iOS/LayoutView.cs @@ -7,6 +7,8 @@ namespace Microsoft.Maui.Platform { public class LayoutView : MauiView { + bool _userInteractionEnabled; + // TODO: Possibly reconcile this code with ViewHandlerExtensions.MeasureVirtualView // If you make changes here please review if those changes should also // apply to ViewHandlerExtensions.MeasureVirtualView @@ -52,5 +54,35 @@ public override void WillRemoveSubview(UIView uiview) internal Func? CrossPlatformMeasure { get; set; } internal Func? CrossPlatformArrange { get; set; } + + public override UIView HitTest(CGPoint point, UIEvent? uievent) + { + var result = base.HitTest(point, uievent); + + if (!_userInteractionEnabled && this.Equals(result)) + { + // If user interaction is disabled (IOW, if the corresponding Layout is InputTransparent), + // then we exclude the LayoutView itself from hit testing. But it's children are valid + // hit testing targets. + + return null!; + } + + return result; + } + + public override bool UserInteractionEnabled + { + get => base.UserInteractionEnabled; + set + { + // We leave the base UIE value true no matter what, so that hit testing will find children + // of the LayoutView. But we track the intended value so we can use it during hit testing + // to ignore the LayoutView itself, if necessary. + + base.UserInteractionEnabled = true; + _userInteractionEnabled = value; + } + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/HandlerTestBaseOfT.iOS.cs b/src/Core/tests/DeviceTests/Handlers/HandlerTestBaseOfT.iOS.cs index c5cd12d06767..fd026ac14d97 100644 --- a/src/Core/tests/DeviceTests/Handlers/HandlerTestBaseOfT.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/HandlerTestBaseOfT.iOS.cs @@ -112,6 +112,13 @@ public async Task RotationInitializeCorrectly(float rotation) [InlineData(false)] public async Task InputTransparencyInitializesCorrectly(bool inputTransparent) { + if (typeof(TStub) == typeof(LayoutStub)) + { + // The platform type for Layouts (LayoutView) always has UserInteractionEnabled + // to allow for its children to be interacted with + return; + } + var view = new TStub() { InputTransparent = inputTransparent From 689dcaa0f89c733b630a97b22c32f30f64a87e5c Mon Sep 17 00:00:00 2001 From: Lutz Roeder Date: Tue, 17 May 2022 18:09:41 -0700 Subject: [PATCH 10/14] Enable creating blank issues (#7282) --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index b0f3917fc8f6..df61e234afd2 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: false +blank_issues_enabled: true contact_links: - name: .NET MAUI Community Support url: https://docs.microsoft.com/answers/topics/dotnet-maui.html From cb2014b6ffb8e097be1b5301f0a9a7cace11f213 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 18 May 2022 06:14:05 -0500 Subject: [PATCH 11/14] Cleanup cake file, add targets, fix for iOS Pair (#7241) * Cleanup cake file, add targets, fix for iOS Pair * - fix sln argument * - add dogfood ps1 * - pack maui before opening external sln * - add "pack" argument * - exit earlier * - script cleanup * - move dogfood into cake and condtionally run * - fix pack checks * - open VS even if build tasks fail to build * - change to warning * - change CI check to error * - set dotnet_root * - fix vsmac * - changes to omnisharp * - switch to flags * - fix clean --- build.cake | 1252 +------------------------------------- eng/cake/dotnet.cake | 171 ++++-- eng/cake/helpers.cake | 36 ++ eng/package.ps1 | 5 + src/DotNet/DotNet.csproj | 8 + 5 files changed, 169 insertions(+), 1303 deletions(-) diff --git a/build.cake b/build.cake index 995fee1b8569..141cf39e3d99 100644 --- a/build.cake +++ b/build.cake @@ -35,1017 +35,11 @@ PowerShell: // ARGUMENTS ////////////////////////////////////////////////////////////////////// -string agentName = EnvironmentVariable("AGENT_NAME", ""); -bool isCIBuild = !String.IsNullOrWhiteSpace(agentName); -string configuration = GetBuildVariable("configuration", GetBuildVariable("BUILD_CONFIGURATION", "DEBUG")); -DirectoryPath artifactStagingDirectory = MakeAbsolute(Directory(EnvironmentVariable("BUILD_ARTIFACTSTAGINGDIRECTORY", "artifacts"))); -DirectoryPath logDirectory = MakeAbsolute(Directory(EnvironmentVariable("LogDirectory", $"{artifactStagingDirectory}/logs"))); -DirectoryPath testResultsDirectory = MakeAbsolute(Directory(EnvironmentVariable("TestResultsDirectory", $"{artifactStagingDirectory}/test-results"))); -DirectoryPath diffDirectory = MakeAbsolute(Directory(EnvironmentVariable("ApiDiffDirectory", $"{artifactStagingDirectory}/api-diff"))); -DirectoryPath tempDirectory = MakeAbsolute(Directory(EnvironmentVariable("AGENT_TEMPDIRECTORY", EnvironmentVariable("TEMP", EnvironmentVariable("TMPDIR", "../maui-temp")) + "/" + Guid.NewGuid()))); - -var target = Argument("target", "Default"); +var target = Argument("target", "Default"); if(String.IsNullOrWhiteSpace(target)) target = "Default"; -var IOS_SIM_NAME = GetBuildVariable("IOS_SIM_NAME", "iPhone 8"); -var IOS_SIM_RUNTIME = GetBuildVariable("IOS_SIM_RUNTIME", "com.apple.CoreSimulator.SimRuntime.iOS-14-4"); -var IOS_CONTROLGALLERY = "src/Compatibility/ControlGallery/src/iOS/"; -var IOS_CONTROLGALLERY_PROJ = $"{IOS_CONTROLGALLERY}Compatibility.ControlGallery.iOS.csproj"; -var IOS_TEST_PROJ = "./src/Compatibility/ControlGallery/test/iOS.UITests/Compatibility.ControlGallery.iOS.UITests.csproj"; -var IOS_TEST_LIBRARY = Argument("IOS_TEST_LIBRARY", $"./src/Compatibility/ControlGallery/test/iOS.UITests/bin/{configuration}/Microsoft.Maui.Controls.iOS.UITests.dll"); -var IOS_IPA_PATH = Argument("IOS_IPA_PATH", $"./src/Compatibility/ControlGallery/src/iOS/bin/iPhoneSimulator/{configuration}/CompatibilityControlGalleryiOS.app"); -var IOS_BUNDLE_ID = "com.microsoft.mauicompatibilitygallery"; -var IOS_BUILD_IPA = Argument("IOS_BUILD_IPA", (target == "cg-ios-deploy") ? true : (false || isCIBuild) ); -Guid IOS_SIM_UDID = Argument("IOS_SIM_UDID", Guid.Empty); - -var UWP_PACKAGE_ID = "0d4424f6-1e29-4476-ac00-ba22c3789cb6"; -var UWP_TEST_LIBRARY = GetBuildVariable("UWP_TEST_LIBRARY", $"./src/Compatibility/ControlGallery/test/Xamarin.Forms.Core.Windows.UITests/bin/{configuration}/Xamarin.Forms.Core.Windows.UITests.dll"); -var UWP_PFX_PATH = Argument("UWP_PFX_PATH", "Xamarin.Forms.ControlGallery.WindowsUniversal\\Xamarin.Forms.ControlGallery.WindowsUniversal_TemporaryKey.pfx"); -var UWP_APP_PACKAGES_PATH = Argument("UWP_APP_PACKAGES_PATH", "*/AppPackages/"); -var UWP_APP_DRIVER_INSTALL_PATH = Argument("UWP_APP_DRIVER_INSTALL_PATH", "https://github.com/microsoft/WinAppDriver/releases/download/v1.2-RC/WindowsApplicationDriver.msi"); - -var ANDROID_BUNDLE_ID = "com.microsoft.mauicompatibilitygallery"; -var ANDROID_CONTROLGALLERY = "src/Compatibility/ControlGallery/src/Android/"; -var ANDROID_CONTROLGALLERY_PROJ = $"{ANDROID_CONTROLGALLERY}Compatibility.ControlGallery.Android.csproj"; -var ANDROID_RENDERERS = Argument("ANDROID_RENDERERS", "FAST"); -var ANDROID_TEST_PROJ = "./src/Compatibility/ControlGallery/test/Android.UITests/Compatibility.ControlGallery.Android.UITests.csproj"; - -var BUILD_TASKS_PROJ ="._Microsoft.Maui.BuildTasks.sln"; - -var XamarinFormsVersion = Argument("XamarinFormsVersion", ""); -var packageVersion = GetBuildVariable("packageVersion", "0.1.0-p2"); -var releaseChannelArg = GetBuildVariable("CHANNEL", "Stable"); -var teamProject = GetBuildVariable("TeamProject", GetBuildVariable("SYSTEM_TEAMPROJECT", "")); -bool isHostedAgent = agentName.StartsWith("Azure Pipelines") || agentName.StartsWith("Hosted Agent"); -var localDotnet = GetBuildVariable("workloads", (target == "VS-WINUI") ? "global" : "local") == "local"; - -var vsVersion = GetBuildVariable("VS", ""); - -var MAUI_SLN = "./._Microsoft.Maui.sln"; - -var CONTROLGALLERY_SLN = "./._ControlGallery.sln"; - -string defaultUnitTestWhere = ""; - -if(target.ToLower().Contains("uwp")) - defaultUnitTestWhere = "cat != UwpIgnore"; - -var NUNIT_TEST_WHERE = Argument("NUNIT_TEST_WHERE", defaultUnitTestWhere); -NUNIT_TEST_WHERE = ParseDevOpsInputs(NUNIT_TEST_WHERE); - -var ANDROID_HOME = EnvironmentVariable("ANDROID_HOME") ?? - (IsRunningOnWindows () ? "C:\\Program Files (x86)\\Android\\android-sdk\\" : ""); - -string MSBuildExe = Argument("msbuild", EnvironmentVariable("MSBUILD_EXE", "")); -string MSBuildArgumentsENV = EnvironmentVariable("MSBuildArguments", ""); -string MSBuildArgumentsARGS = Argument("MSBuildArguments", ""); -string MSBuildArguments; - -MSBuildArguments = $"{MSBuildArgumentsENV} {MSBuildArgumentsARGS}"; - -Information("MSBuildArguments: {0}", MSBuildArguments); - -string androidEmulators = EnvironmentVariable("ANDROID_EMULATORS", ""); - -string androidSdks = EnvironmentVariable("ANDROID_API_SDKS", - // build/platform tools - "build-tools;29.0.3," + - "build-tools;30.0.2," + - "platform-tools," + - // apis - "platforms;android-26," + - "platforms;android-27," + - "platforms;android-28," + - "platforms;android-29," + - "platforms;android-30," + - "platforms;android-31," + - // emulators - androidEmulators); - -Information("ANDROID_API_SDKS: {0}", androidSdks); -string[] androidSdkManagerInstalls = androidSdks.Split(','); - -(string name, string location, string featureList)[] windowsSdksInstalls = new (string name, string location, string featureList)[] -{ - ("10.0.19041.0", "https://go.microsoft.com/fwlink/p/?linkid=2120843", "OptionId.WindowsPerformanceToolkit OptionId.WindowsDesktopDebuggers OptionId.AvrfExternal OptionId.WindowsSoftwareLogoToolkit OptionId.MSIInstallTools OptionId.SigningTools OptionId.UWPManaged OptionId.UWPCPP OptionId.UWPLocalized OptionId.DesktopCPPx86 OptionId.DesktopCPPx64 OptionId.DesktopCPParm OptionId.DesktopCPParm64"), - ("10.0.18362.0", "https://go.microsoft.com/fwlink/?linkid=2083338", "+"), - ("10.0.17763.0", "https://go.microsoft.com/fwlink/p/?linkid=2033908", "+"), - ("10.0.16299.0", "https://go.microsoft.com/fwlink/p/?linkid=864422", "+"), - ("10.0.14393.0", "https://go.microsoft.com/fwlink/p/?LinkId=838916", "+") -}; - -string[] netFrameworkSdksLocalInstall = new string[] -{ - "https://go.microsoft.com/fwlink/?linkid=2099470", //NET461 SDK - "https://go.microsoft.com/fwlink/?linkid=874338", //NET472 SDK - "https://go.microsoft.com/fwlink/?linkid=2099465", //NET47 - "https://download.microsoft.com/download/A/1/D/A1D07600-6915-4CB8-A931-9A980EF47BB7/NDP47-DevPack-KB3186612-ENU.exe", //net47 targeting pack - "https://go.microsoft.com/fwlink/?linkid=2088517", //NET48 SDK -}; - -// these don't run on CI -(string msiUrl, string cabUrl)[] netframeworkMSI = new (string msiUrl, string cabUrl)[] -{ - ( - "https://download.visualstudio.microsoft.com/download/pr/34dae2b3-314f-465e-aba0-0a862c29638e/b2bc986f304acdd76fcd8f910012b656/sdk_tools462.msi", - "https://download.visualstudio.microsoft.com/download/pr/6283f4a0-36b3-4336-a6f2-c5afd9f8fdbb/ffbe35e429f7d5c1d3777d03b2f38a24/sdk_tools462.cab" - ), - ( - "https://download.visualstudio.microsoft.com/download/pr/0d63c72c-9341-4de6-b493-dc7ad0d01246/f16b6402b8f8fb3b95dde5c1c2e5a2b4/sdk_tools461.msi", - "https://download.visualstudio.microsoft.com/download/pr/3dc58ffd-d515-43a4-87bd-2aba395eab17/5bff8f781c9843d64bd2367898395c5e/sdk_tools461.cab" - ), - ( - "https://download.visualstudio.microsoft.com/download/pr/9d14aa59-3f7f-4fe6-85e9-3bc31031e1f2/88b90ec9d096ec382a001e1fbd4a6be8/sdk_tools472.msi", - "https://download.visualstudio.microsoft.com/download/pr/77f1d250-f253-4c48-849c-0f08c9c11e77/ab2aa8f856e686cd4ad1c921742f2eeb/sdk_tools472.cab" - ) -}; - -Information ("XamarinFormsVersion: {0}", XamarinFormsVersion); -Information ("ANDROID_RENDERERS: {0}", ANDROID_RENDERERS); -Information ("configuration: {0}", configuration); -Information ("ANDROID_HOME: {0}", ANDROID_HOME); -Information ("Team Project: {0}", teamProject); -Information ("Agent.Name: {0}", agentName); -Information ("isCIBuild: {0}", isCIBuild); -Information ("artifactStagingDirectory: {0}", artifactStagingDirectory); -Information("tempDirectory: {0}", tempDirectory); -Information("NUNIT_TEST_WHERE: {0}", NUNIT_TEST_WHERE); -Information("TARGET: {0}", target); -Information("MSBUILD: {0}", MSBuildExe); -Information("vsVersion: {0}", vsVersion); -Information("localDotnet: {0}", localDotnet); -Information("dotnet: {0}", GetBuildVariable("dotnet", "")); -Information("workloads: {0}", GetBuildVariable("workloads", "")); - - -var releaseChannel = ReleaseChannel.Stable; -if(releaseChannelArg == "Preview") -{ - releaseChannel = ReleaseChannel.Preview; -} - -Information ("Release Channel: {0}", releaseChannel); - -string androidSDK_macos = ""; -string monoSDK_macos = ""; -string iOSSDK_macos = ""; -string macSDK_macos = ""; -string monoPatchVersion = ""; -string monoMajorVersion = ""; -string monoVersion = ""; - -if(releaseChannel == ReleaseChannel.Stable) -{ - if(IsXcodeVersionAtLeast("12.0")) - { - } - else - { - monoMajorVersion = ""; - monoPatchVersion = ""; - iOSSDK_macos = $"https://bosstoragemirror.blob.core.windows.net/wrench/jenkins/d16-7-xcode11.7/3016ffe2b0ee27bf4a2d61e6161430d6bbd62f78/7/package/notarized/xamarin.ios-13.20.3.5.pkg"; - macSDK_macos = $"https://bosstoragemirror.blob.core.windows.net/wrench/jenkins/d16-7-xcode11.7/3016ffe2b0ee27bf4a2d61e6161430d6bbd62f78/7/package/notarized/xamarin.mac-6.20.3.5.pkg"; - } -} - -if(String.IsNullOrWhiteSpace(monoSDK_macos)) -{ - if(String.IsNullOrWhiteSpace(monoPatchVersion)) - monoVersion = $"{monoMajorVersion}"; - else - monoVersion = $"{monoMajorVersion}.{monoPatchVersion}"; - - if(!String.IsNullOrWhiteSpace(monoVersion)) - { - monoSDK_macos = $"https://download.mono-project.com/archive/{monoMajorVersion}/macos-10-universal/MonoFramework-MDK-{monoVersion}.macos10.xamarin.universal.pkg"; - } -} - -string androidSDK_windows = ""; -string iOSSDK_windows = ""; -string monoSDK_windows = ""; -string macSDK_windows = ""; -string android_jdk_11_windows = "https://aka.ms/download-jdk/microsoft-jdk-11.0.12.7.1-windows-x64.msi"; -string android_jdk_11_macos = "https://aka.ms/download-jdk/microsoft-jdk-11.0.12.7.1-macOS-x64.pkg"; -string android_jdk_11_folder = "C:\\Program Files\\Microsoft\\jdk-11.0.12.7-hotspot"; - -androidSDK_macos = EnvironmentVariable("ANDROID_SDK_MAC", androidSDK_macos); -iOSSDK_macos = EnvironmentVariable("IOS_SDK_MAC", iOSSDK_macos); -monoSDK_macos = EnvironmentVariable("MONO_SDK_MAC", monoSDK_macos); -macSDK_macos = EnvironmentVariable("MAC_SDK_MAC", macSDK_macos); - -androidSDK_windows = EnvironmentVariable("ANDROID_SDK_WINDOWS", ""); -iOSSDK_windows = EnvironmentVariable("IOS_SDK_WINDOWS", ""); -monoSDK_windows = EnvironmentVariable("MONO_SDK_WINDOWS", ""); -macSDK_windows = EnvironmentVariable("MAC_SDK_WINDOWS", ""); - -string androidSDK = IsRunningOnWindows() ? androidSDK_windows : androidSDK_macos; -string monoSDK = IsRunningOnWindows() ? monoSDK_windows : monoSDK_macos; -string iosSDK = IsRunningOnWindows() ? iOSSDK_windows : iOSSDK_macos; -string macSDK = IsRunningOnWindows() ? macSDK_windows : macSDK_macos; - - -Information ("androidSDK: {0}", androidSDK); -Information ("monoSDK: {0}", monoSDK); -Information ("macSDK: {0}", macSDK); -Information ("iosSDK: {0}", iosSDK); - -////////////////////////////////////////////////////////////////////// -// TASKS -////////////////////////////////////////////////////////////////////// - -Task("BuildUnitTests") - .IsDependentOn("BuildTasks") - .Description("Builds all necessary projects to run Unit Tests") - .Does(() => -{ - try - { - var msbuildSettings = GetMSBuildSettings(); - var binaryLogger = new MSBuildBinaryLogSettings { - Enabled = isCIBuild - }; - - msbuildSettings.BinaryLogger = binaryLogger; - binaryLogger.FileName = $"{artifactStagingDirectory}/Maui.Controls-{configuration}.binlog"; - MSBuild("./._Microsoft.Maui.sln", msbuildSettings.WithRestore()); - } - catch(Exception) - { - if(IsRunningOnWindows()) - throw; - } -}); - -Task("provision-macsdk") - .Description("Install Xamarin.Mac SDK") - .Does(async () => - { - if(!IsRunningOnWindows()) - { - if(!String.IsNullOrWhiteSpace(macSDK)) - await Boots(macSDK); - else - await Boots (Product.XamarinMac, releaseChannel); - } - else if(!String.IsNullOrWhiteSpace(macSDK)) - await Boots(macSDK); - }); - -Task("provision-iossdk") - .Description("Install Xamarin.iOS SDK") - .Does(async () => - { - if (!IsRunningOnWindows()) { - if(!String.IsNullOrWhiteSpace(iosSDK)) - await Boots(iosSDK); - else - await Boots (Product.XamariniOS, releaseChannel); - } - else if(!String.IsNullOrWhiteSpace(iosSDK)) - await Boots(iosSDK); - }); - -Task("provision-androidsdk") - .Description("Install Xamarin.Android SDK") - .Does(async () => - { - Information ("ANDROID_HOME: {0}", ANDROID_HOME); - - if(androidSdkManagerInstalls.Length > 0) - { - Information("Updating Android SDKs"); - var androidSdkSettings = new AndroidSdkManagerToolSettings { - SkipVersionCheck = true - }; - - if(!String.IsNullOrWhiteSpace(ANDROID_HOME)) - androidSdkSettings.SdkRoot = ANDROID_HOME; - - try{ - AcceptLicenses (androidSdkSettings); - } - catch(Exception exc) - { - Information("AcceptLicenses: {0}", exc); - } - - try{ - AndroidSdkManagerUpdateAll (androidSdkSettings); - } - catch(Exception exc) - { - Information("AndroidSdkManagerUpdateAll: {0}", exc); - } - - try{ - AcceptLicenses (androidSdkSettings); - } - catch(Exception exc) - { - Information("AcceptLicenses: {0}", exc); - } - - try{ - AndroidSdkManagerInstall (androidSdkManagerInstalls, androidSdkSettings); - } - catch(Exception exc) - { - Information("AndroidSdkManagerInstall: {0}", exc); - } - } - - if (!IsRunningOnWindows ()) - { - - await Boots(android_jdk_11_macos); - - if(!String.IsNullOrWhiteSpace(androidSDK)) - { - await Boots (androidSDK); - } - else - { - await Boots (Product.XamarinAndroid, releaseChannel); - } - } - else - { - if(!DirectoryExists(android_jdk_11_folder)) - { - await Boots(android_jdk_11_windows); - } - if(!String.IsNullOrWhiteSpace(androidSDK)) - { - await Boots (androidSDK); - } - } - }); - -Task("provision-monosdk") - .Description("Install Mono SDK") - .Does(async () => - { - if(!IsRunningOnWindows()) - { - if(!String.IsNullOrWhiteSpace(monoSDK)) - await Boots(monoSDK); - else - await Boots (Product.Mono, releaseChannel); - } - else if(!String.IsNullOrWhiteSpace(monoSDK)) - await Boots(monoSDK); - }); - -Task("provision-windowssdk") - .Description("Install Windows SDK") - .Does(() => - { - if(IsRunningOnWindows() && !isHostedAgent) - { - int i = 0; - foreach(var windowsSdk in windowsSdksInstalls) - { - string sdkPath = System.IO.Path.Combine(@"C:\Program Files (x86)\Windows Kits\10\Platforms\UAP", windowsSdk.name); - if(DirectoryExists(sdkPath) && GetFiles(System.IO.Path.Combine(sdkPath, "*.*")).Count() > 0) - { - Information("Already Installed: {0}", sdkPath); - continue; - } - - - Information("Installing: {0}", sdkPath); - string installUrl = windowsSdk.location; - string installerPath = $"{System.IO.Path.GetTempPath()}" + $"WindowsSDK{i}.exe"; - DownloadFile(installUrl, installerPath); - - var result = StartProcess(installerPath, new ProcessSettings { - Arguments = new ProcessArgumentBuilder() - .Append(@"/features ") - .Append(windowsSdk.featureList) - .Append(@" /q") - } - ); - - i++; - } - } - }); - -Task("provision-netsdk-local") - .Description("Install .NET SDK") - .Does(() => - { - if(IsRunningOnWindows() && (!isCIBuild || target == "provision-netsdk-local")) - { - foreach(var installUrl in netframeworkMSI) - { - string msiUrl = installUrl.msiUrl; - string cabUrl = installUrl.cabUrl; - - - string cabName = cabUrl.Split('/').Last(); - string msiName = msiUrl.Split('/').Last(); - string cabPath = $"{System.IO.Path.GetTempPath()}{cabName}"; - - Information("Downloading: {0} to {1}", cabUrl, cabPath); - DownloadFile(cabUrl, cabPath); - InstallMsiOrExe(msiUrl, null, msiName); - } - - int i = 0; - foreach(var installUrl in netFrameworkSdksLocalInstall) - { - Information("Installing: {0}", installUrl); - string installerPath = $"{System.IO.Path.GetTempPath()}" + $"netSDKS{i}.exe"; - DownloadFile(installUrl, installerPath); - - var result = StartProcess(installerPath, new ProcessSettings { - Arguments = new ProcessArgumentBuilder() - .Append(@"/quiet") - } - ); - - i++; - } - } - }); - -Task ("cg-uwp") - .IsDependentOn("BuildTasks") - .Does (() => -{ - MSBuild ("Xamarin.Forms.ControlGallery.WindowsUniversal\\Xamarin.Forms.ControlGallery.WindowsUniversal.csproj", - GetMSBuildSettings().WithRestore()); -}); - -Task ("cg-uwp-build-tests") - .IsDependentOn("BuildTasks") - .Does (() => -{ - MSBuild ("Xamarin.Forms.ControlGallery.WindowsUniversal\\Xamarin.Forms.ControlGallery.WindowsUniversal.csproj", - GetMSBuildSettings(null) - .WithProperty("AppxBundlePlatforms", "x86") - .WithProperty("AppxBundle", "Always") - .WithProperty("UapAppxPackageBuildMode", "StoreUpload") - .WithProperty("AppxPackageSigningEnabled", "true") - .WithProperty("PackageCertificateThumbprint", "a59087cc92a9a8117ffdb5255eaa155748f9f852") - .WithProperty("PackageCertificateKeyFile", "Xamarin.Forms.ControlGallery.WindowsUniversal_TemporaryKey.pfx") - .WithProperty("PackageCertificatePassword", "") - // The platform unit tests can't run when UseDotNetNativeToolchain is set to true so we force it off here - .WithProperty("UseDotNetNativeToolchain", "false") - .WithRestore() - ); - - MSBuild("Xamarin.Forms.Core.Windows.UITests\\Xamarin.Forms.Core.Windows.UITests.csproj", - GetMSBuildSettings(buildConfiguration:"Debug").WithRestore()); -}); - -Task ("cg-uwp-deploy") - .WithCriteria(IsRunningOnWindows()) - .Does (() => -{ - var uninstallPS = new Action (() => { - try { - StartProcess ("powershell", - "$app = Get-AppxPackage -Name " + UWP_PACKAGE_ID + "; if ($app) { Remove-AppxPackage -Package $app.PackageFullName }"); - } catch { } - }); - // Try to uninstall the app if it exists from before - uninstallPS(); - - StartProcess("certutil", "-f -p \"\" -importpfx \"" + UWP_PFX_PATH + "\""); - - // Install the appx - var dependencies = GetFiles(UWP_APP_PACKAGES_PATH + "*/Dependencies/x86/*.appx"); - - foreach (var dep in dependencies) { - try - { - Information("Installing Dependency appx: {0}", dep); - StartProcess("powershell", "Add-AppxPackage -Path \"" + MakeAbsolute(dep).FullPath + "\""); - } - catch(Exception exc) - { - Information("Error: {0}", exc); - } - } - - var appxBundlePath = GetFiles(UWP_APP_PACKAGES_PATH + "*/*.appxbundle").First (); - Information("Installing appx: {0}", appxBundlePath); - StartProcess ("powershell", "Add-AppxPackage -Path \"" + MakeAbsolute(appxBundlePath).FullPath + "\""); -}); - -Task("cg-uwp-run-tests") - .IsDependentOn("cg-uwp-build-tests") - .IsDependentOn("cg-uwp-deploy") - .IsDependentOn("provision-uitests-uwp") - .IsDependentOn("_cg-uwp-run-tests"); - -Task("_cg-uwp-run-tests") - .Does((ctx) => - { - System.Diagnostics.Process process = null; - if(!isHostedAgent) - { - try - { - var info = new System.Diagnostics.ProcessStartInfo(@"C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe") - { - }; - - process = System.Diagnostics.Process.Start(info); - } - catch(Exception exc) - { - Information("Failed: {0}", exc); - } - } - - var settings = new NUnit3Settings { - Params = new Dictionary() - { - {"IncludeScreenShots", "true"} - } - }; - - - try - { - RunTests(UWP_TEST_LIBRARY, settings, ctx); - } - finally - { - try - { - process?.Kill(); - } - catch{} - } - - }); - -Task("cg-uwp-run-tests-ci") - .IsDependentOn("provision-windowssdk") - .IsDependentOn("cg-uwp-deploy") - .IsDependentOn("_cg-uwp-run-tests") - .Does(() => - { - }); - -Task("provision-uitests-uwp") - .WithCriteria(IsRunningOnWindows() && !isHostedAgent) - .Description("Installs and Starts WindowsApplicationDriver. Use WinAppDriverPath to specify WinAppDriver Location.") - .Does(() => - { - string installPath = Argument("WinAppDriverPath", @"C:\Program Files (x86)\"); - string driverPath = System.IO.Path.Combine(installPath, "Windows Application Driver"); - if(!DirectoryExists(driverPath)) - { - try{ - InstallMsiOrExe(UWP_APP_DRIVER_INSTALL_PATH, installPath); - } - catch(Exception e) - { - Information("Failed to Install Win App Driver: {0}", e); - } - } - }); - - -async Task InstallMsiWithBoots(string msiFile, string installTo = null, string fileName = "InstallFile.msi") -{ - bool success = false; - - try - { - await Boots(msiFile); - success = true; - } - catch (System.Exception e) - { - Information("Boots failed: {0}", e); - } - - - if(success) - return; - - try - { - InstallMsiOrExe(msiFile, installTo, fileName, !isCIBuild); - success = true; - } - catch (System.Exception e) - { - Information("Our attempt failed: {0}", e); - } -} - -void InstallMsiOrExe(string msiFile, string installTo = null, string fileName = "InstallFile.msi", bool interactive = false) -{ - if(msiFile.EndsWith(".exe") && fileName == "InstallFile.msi") - fileName = "InstallFile.exe"; - - string installerPath = $"{System.IO.Path.GetTempPath()}{fileName}"; - - try - { - Information ("Installing: {0}", msiFile); - DownloadFile(msiFile, installerPath); - Information("File Downloaded To: {0}", installerPath); - int result = -1; - - if(msiFile.EndsWith(".exe")) - { - result = StartProcess(installerPath, new ProcessSettings { - Arguments = new ProcessArgumentBuilder() - .Append(@" /q") - } - ); - } - else{ - var argumentBuilder = - new ProcessArgumentBuilder() - .Append("/a") - .Append(installerPath); - - if(!interactive) - argumentBuilder = argumentBuilder.Append("/qn"); - - if(!String.IsNullOrWhiteSpace(installTo)) - { - Information("Installing into: {0}", installTo); - argumentBuilder = argumentBuilder.Append("TARGETDIR=\"" + installTo + "\""); - } - - result = StartProcess("msiexec", new ProcessSettings { - Arguments = argumentBuilder - }); - } - - if(result != 0) - throw new Exception("Failed to install: " + msiFile); - - Information("File Installed: {0}", result); - } - catch(Exception exc) - { - Information("Failed to install {0} make sure you are running script as admin {1}", msiFile, exc); - throw; - } - finally{ - DeleteFile(installerPath); - - } -} - -Task("provision") - .Description("Install SDKs required to build project") - .IsDependentOn("provision-macsdk") - .IsDependentOn("provision-iossdk") - .IsDependentOn("provision-androidsdk") - .IsDependentOn("provision-netsdk-local") - .IsDependentOn("provision-windowssdk") - .IsDependentOn("provision-monosdk"); // always provision monosdk last otherwise CI might fail - -Task("provision-powershell").Does(()=> { - var settings = new DotNetCoreToolSettings - { - DiagnosticOutput = true, - ArgumentCustomization = args=>args.Append("install --global PowerShell") - }; - - DotNetCoreTool("tool", settings); -}); - -Task("Restore") - .Description($"Restore target on {MAUI_SLN}") - .Does(() => - { - try{ - MSBuild(MAUI_SLN, GetMSBuildSettings().WithTarget("restore")); - } - catch{ - // ignore restore errors that come from uwp - if(IsRunningOnWindows()) - throw; - } - }); - -Task("WriteGoogleMapsAPIKey") - .Description("Write GoogleMapsAPIKey to Android Control Gallery") - .Does(() => - { - string GoogleMapsAPIKey = Argument("GoogleMapsAPIKey", ""); - - if(!String.IsNullOrWhiteSpace(GoogleMapsAPIKey)) - { - Information("Writing GoogleMapsAPIKey"); - System.IO.File.WriteAllText($"{ANDROID_CONTROLGALLERY}/Properties/MapsKey.cs", "[assembly: Android.App.MetaData(\"com.google.android.maps.v2.API_KEY\", Value = \"" + GoogleMapsAPIKey + "\")]"); - } - }); - -Task("BuildTasks") - .Description($"Build {BUILD_TASKS_PROJ}") - .Does(() => -{ - MSBuild(BUILD_TASKS_PROJ, GetMSBuildSettings().WithRestore()); -}); - -Task("Build") - .Description("Builds all necessary projects to run Control Gallery") - .IsDependentOn("Restore") - .Does(() => -{ - try{ - MSBuild(MAUI_SLN, GetMSBuildSettings().WithRestore()); - } - catch(Exception) - { - if(IsRunningOnWindows()) - throw; - } -}); - -Task("Android100") - .Description("Builds Monodroid10.0 targets") - .Does(() => - { - MSBuild(MAUI_SLN, - GetMSBuildSettings() - .WithRestore() - .WithProperty("AndroidTargetFrameworks", "MonoAndroid10.0")); - }); - -Task("VS") - .Description("Builds projects necessary so solution compiles on VS Preview") - .IsDependentOn("Clean") - .IsDependentOn("VSMAC") - .IsDependentOn("VSWINDOWS"); - -Task("VS-CG") - .Description("Builds projects necessary so solution compiles on VS") - .IsDependentOn("Clean") - .IsDependentOn("VSMAC") - .IsDependentOn("VSWINDOWS"); - -Task("VS-CG-STABLE") - .Description("Builds projects necessary so solution compiles on VS") - .IsDependentOn("Clean") - .IsDependentOn("VSMAC") - .IsDependentOn("VSWINDOWS"); - -Task("VS-STABLE") - .Description("Builds projects necessary so solution compiles on VS Stable") - .IsDependentOn("Clean") - .IsDependentOn("VSMAC") - .IsDependentOn("VSWINDOWS"); - -Task("VSWINDOWS") - .Description("Builds projects necessary so solution compiles on VS Windows") - .IsDependentOn("BuildTasks") - .WithCriteria(IsRunningOnWindows()) - .Does(() => - { - bool includePrerelease = !target.ToLower().Contains("stable"); - - if (target.ToLower().StartsWith("vs-cg")) - { - MSBuild("._Compatibility.ControlGallery.sln", - GetMSBuildSettings() - .WithRestore()); - StartVisualStudio("._Compatibility.ControlGallery.sln", includePrerelease: includePrerelease); - } - else - { - MSBuild(@"src\Compatibility\Core\src\Compatibility.csproj", - GetMSBuildSettings() - .WithProperty("BuildForLegacy", "true") - .WithRestore()); - StartVisualStudio("._Microsoft.Maui.sln", includePrerelease: includePrerelease); - } - }); - -Task("VSMAC") - .Description("Builds projects necessary so solution compiles on VSMAC") - .WithCriteria(!IsRunningOnWindows()) - .IsDependentOn("BuildTasks") - .Does(() => - { - - string sln = "._Microsoft.Maui.sln"; - if (target == "VS-CG") - sln = "._Compatibility.ControlGallery.sln"; - - MSBuild("src/Core/src/Core.csproj", - GetMSBuildSettings() - .WithRestore()); - - MSBuild("src/Essentials/src/Essentials.csproj", - GetMSBuildSettings() - .WithRestore()); - - MSBuild("src/SingleProject/Resizetizer/src/Resizetizer.csproj", GetMSBuildSettings().WithRestore()); - - StartVisualStudio(sln); - }); - -Task("cg-android") - .Description("Builds Android Control Gallery") - .IsDependentOn("WriteGoogleMapsAPIKey") - .IsDependentOn("BuildTasks") - .Does(() => - { - var buildSettings = GetMSBuildSettings(); - - buildSettings = buildSettings.WithRestore(); - - if(isCIBuild) - { - buildSettings = buildSettings.WithTarget("Rebuild").WithTarget("SignAndroidPackage"); - var binaryLogger = new MSBuildBinaryLogSettings { - Enabled = true - }; - - buildSettings.BinaryLogger = binaryLogger; - binaryLogger.FileName = $"{artifactStagingDirectory}/android-{ANDROID_RENDERERS}.binlog"; - } - - MSBuild(ANDROID_CONTROLGALLERY_PROJ, buildSettings); - }); - -Task("cg-android-build-tests") - .IsDependentOn("BuildTasks") - .Does(() => - { - var buildSettings = GetMSBuildSettings(); - - buildSettings = buildSettings.WithRestore(); - - if(isCIBuild) - { - var binaryLogger = new MSBuildBinaryLogSettings { - Enabled = true, - FileName = $"{artifactStagingDirectory}/android-uitests.binlog" - }; - - buildSettings.BinaryLogger = binaryLogger; - } - - MSBuild(ANDROID_TEST_PROJ, buildSettings); - }); - -Task("cg-android-vs") - .Description("Builds Android Control Gallery and open VS") - .IsDependentOn("cg-android") - .Does(() => - { - StartVisualStudio(); - }); - -Task("cg-ios") - .Description("Builds iOS Control Gallery and open VS") - .IsDependentOn("BuildTasks") - .Does(() => - { - var buildSettings = - GetMSBuildSettings(null) - .WithProperty("BuildIpa", $"{IOS_BUILD_IPA}"); - - buildSettings = buildSettings.WithRestore(); - - if(isCIBuild) - { - var binaryLogger = new MSBuildBinaryLogSettings { - Enabled = true - }; - - buildSettings.BinaryLogger = binaryLogger; - binaryLogger.FileName = $"{artifactStagingDirectory}/ios-cg.binlog"; - } - - MSBuild(IOS_CONTROLGALLERY_PROJ, buildSettings); - }); - -Task("cg-ios-vs") - .Description("Builds iOS Control Gallery and open VS") - .IsDependentOn("cg-ios") - .Does(() => - { - StartVisualStudio(); - }); - -Task("cg-ios-build-tests") - .IsDependentOn("BuildTasks") - .Does(() => - { - // the UI Tests all reference the galleries so those get built as a side effect of building the - // ui tests - var buildSettings = - GetMSBuildSettings(null, configuration) - .WithProperty("MtouchArch", "x86_64") - .WithProperty("iOSPlatform", "iPhoneSimulator") - .WithProperty("BuildIpa", $"true") - .WithProperty("CI", $"true") - .WithRestore(); - - if(isCIBuild) - { - var binaryLogger = new MSBuildBinaryLogSettings { - Enabled = true, - FileName = $"{artifactStagingDirectory}/ios-uitests.binlog" - }; - - buildSettings.BinaryLogger = binaryLogger; - } - - MSBuild(IOS_TEST_PROJ, buildSettings); - }); - -Task("cg-ios-run-tests") - .IsDependentOn("cg-ios-build-tests") - .IsDependentOn("cg-ios-deploy") - .IsDependentOn("_cg-ios-run-tests"); - -Task("_cg-ios-run-tests") - .Does((ctx) => - { - var sim = GetIosSimulator(); - - var settings = new NUnit3Settings { - Params = new Dictionary() - { - {"UDID", GetIosSimulator().UDID}, - {"IncludeScreenShots", "true"} - } - }; - - if(isCIBuild) - { - Information("defaults write com.apple.CrashReporter DialogType none"); - IEnumerable redirectedStandardOutput; - StartProcess("defaults", - new ProcessSettings { - Arguments = new ProcessArgumentBuilder().Append(@"write com.apple.CrashReporter DialogType none"), - RedirectStandardOutput = true - }, - out redirectedStandardOutput - ); - - - foreach (var item in redirectedStandardOutput) - { - Information(item); - } - } - - RunTests(IOS_TEST_LIBRARY, settings, ctx); - }); - -Task("cg-ios-run-tests-ci") - .IsDependentOn("cg-ios-deploy") - .IsDependentOn("_cg-ios-run-tests") - .Does(() => - { - }); - -Task ("cg-ios-deploy") - .Does (() => -{ - // Look for a matching simulator on the system - var sim = GetIosSimulator(); - - //ShutdownAndResetiOSSimulator(sim); - - // Boot the simulator - Information("Booting: {0} ({1} - {2})", sim.Name, sim.Runtime, sim.UDID); - if (!sim.State.ToLower().Contains ("booted")) - BootAppleSimulator (sim.UDID); - - // Wait for it to be booted - var booted = false; - for (int i = 0; i < 100; i++) { - if (ListAppleSimulators().Any (s => s.UDID == sim.UDID && s.State.ToLower().Contains("booted"))) { - booted = true; - break; - } - System.Threading.Thread.Sleep(1000); - } - - // Install the IPA that was previously built - var ipaPath = new FilePath(IOS_IPA_PATH); - Information ("Installing: {0}", ipaPath); - InstalliOSApplication(sim.UDID, MakeAbsolute(ipaPath).FullPath); - - - // Launch the IPA - Information("Launching: {0}", IOS_BUNDLE_ID); - LaunchiOSApplication(sim.UDID, IOS_BUNDLE_ID); -}); - ////////////////////////////////////////////////////////////////////// // TASK TARGETS ////////////////////////////////////////////////////////////////////// @@ -1056,246 +50,4 @@ Task("Default").IsDependentOn("dotnet").IsDependentOn("dotnet-pack"); // EXECUTION ////////////////////////////////////////////////////////////////////// -RunTarget(target); - -void RunTests(string unitTestLibrary, NUnit3Settings settings, ICakeContext ctx) -{ - try - { - if(!String.IsNullOrWhiteSpace(NUNIT_TEST_WHERE)) - { - settings.Where = NUNIT_TEST_WHERE; - } - - NUnit3(new [] { unitTestLibrary }, settings); - } - catch - { - SetTestResultsEnvironmentVariables(); - throw; - } - - SetTestResultsEnvironmentVariables(); - - void SetTestResultsEnvironmentVariables() - { - var doc = new System.Xml.XmlDocument(); - doc.Load("TestResult.xml"); - var root = doc.DocumentElement; - - foreach(System.Xml.XmlAttribute attr in root.Attributes) - { - SetEnvironmentVariable($"NUNIT_{attr.Name}", attr.Value, ctx); - } - } -} - -void StartVisualStudio(string sln = "./._Microsoft.Maui.sln", bool includePrerelease = true) -{ - if(isCIBuild) - return; - - if (!String.IsNullOrWhiteSpace(vsVersion)) - includePrerelease = (vsVersion == "preview"); - - if(IsRunningOnWindows()) - { - var vsLatest = VSWhereLatest(new VSWhereLatestSettings { IncludePrerelease = includePrerelease, }); - if (vsLatest == null) - throw new Exception("Unable to find Visual Studio!"); - - StartProcess(vsLatest.CombineWithFilePath("./Common7/IDE/devenv.exe"), sln); - } - else - StartProcess("open", new ProcessSettings{ Arguments = sln }); -} - -MSBuildSettings GetMSBuildSettings( - PlatformTarget? platformTarget = PlatformTarget.MSIL, - string buildConfiguration = null, - bool includePrerelease = false) -{ - var buildSettings = new MSBuildSettings { - PlatformTarget = platformTarget, - MSBuildPlatform = Cake.Common.Tools.MSBuild.MSBuildPlatform.x86, - Configuration = buildConfiguration ?? configuration, - }; - - - if(IsRunningOnWindows()) - { - var vsInstallation = - VSWhereLatest(new VSWhereLatestSettings { Requires = "Microsoft.Component.MSBuild", IncludePrerelease = includePrerelease }) - ?? VSWhereLatest(new VSWhereLatestSettings { Requires = "Microsoft.Component.MSBuild" }); - - if (vsInstallation != null) - { - buildSettings.ToolPath = vsInstallation.CombineWithFilePath(@"MSBuild\Current\Bin\MSBuild.exe"); - if (!FileExists(buildSettings.ToolPath)) - buildSettings.ToolPath = vsInstallation.CombineWithFilePath(@"MSBuild\15.0\Bin\MSBuild.exe"); - } - } - - buildSettings = buildSettings.WithProperty("ANDROID_RENDERERS", $"{ANDROID_RENDERERS}"); - if(!String.IsNullOrWhiteSpace(XamarinFormsVersion)) - { - buildSettings = buildSettings.WithProperty("XamarinFormsVersion", XamarinFormsVersion); - } - - buildSettings.ArgumentCustomization = args => args.Append($"/nowarn:VSX1000 {MSBuildArguments}"); - return buildSettings; -} - -bool IsXcodeVersionAtLeast(string version) -{ - if(IsRunningOnWindows()) - return true; - - return XcodeVersion() >= Version.Parse(version); -} - -Version XcodeVersion() -{ - if(IsRunningOnWindows()) - return null; - - IEnumerable redirectedStandardOutput; - StartProcess("xcodebuild", - new ProcessSettings { - Arguments = new ProcessArgumentBuilder().Append(@"-version"), - RedirectStandardOutput = true - }, - out redirectedStandardOutput - ); - - foreach (var item in redirectedStandardOutput) - { - if(item.Contains("Xcode")) - { - var xcodeVersion = Version.Parse(item.Replace("Xcode", "")); - Information($"Xcode: {xcodeVersion}"); - - return xcodeVersion; - } - } - - return null; -} - -IReadOnlyList iosSimulators = null; - -void ShutdownAndResetiOSSimulator(AppleSimulator sim) -{ - //close all simulators , reset needs simulator to be closed - Information("Shutdown simulators: {0} ({1} - {2}) State: {3}", sim.Name, sim.Runtime, sim.UDID, sim.State); - ShutdownAllAppleSimulators(); - - var shutdown = false; - for (int i = 0; i < 100; i++) { - if (ListAppleSimulators().Any (s => s.UDID == sim.UDID && s.State.ToLower().Contains("shutdown"))) { - shutdown = true; - break; - } - System.Threading.Thread.Sleep(1000); - } - - //Reset the simulator - Information ("Factory reset simulator: {0}", sim.UDID); - EraseAppleSimulator(sim.UDID); -} - -AppleSimulator GetIosSimulator() -{ - if(iosSimulators == null) - { - iosSimulators = ListAppleSimulators (); - foreach (var s in iosSimulators) - { - Information("Info: {0} ({1} - {2} - {3})", s.Name, s.Runtime, s.UDID, s.Availability); - } - - StartProcess("xcrun", new ProcessSettings { - Arguments = new ProcessArgumentBuilder() - .Append(@"simctl list") - } - ); - } - - if(IOS_SIM_UDID != Guid.Empty) - return iosSimulators.First (s => Guid.Parse(s.UDID) == IOS_SIM_UDID); - - // Look for a matching simulator on the system - return iosSimulators.First (s => s.Name == IOS_SIM_NAME && s.Runtime == IOS_SIM_RUNTIME); -} - -public void SetEnvironmentVariable(string key, string value, ICakeContext context) -{ - var buildSystem = context.BuildSystem(); - Information("Setting: {0} to {1}", key, value); - if(isCIBuild) - { - buildSystem.AzurePipelines.Commands.SetVariable(key, value); - } - else - { - System.Environment.SetEnvironmentVariable(key, value); - } -} - -public string ParseDevOpsInputs(string nunitWhere) -{ - var ExcludeCategory = GetBuildVariable("ExcludeCategory", "")?.Replace("\"", ""); - var ExcludeCategory2 = GetBuildVariable("ExcludeCategory2", "")?.Replace("\"", ""); - var IncludeCategory = GetBuildVariable("IncludeCategory", "")?.Replace("\"", ""); - - Information("ExcludeCategory: {0}", ExcludeCategory); - Information("IncludeCategory: {0}", IncludeCategory); - Information("ExcludeCategory2: {0}", ExcludeCategory2); - string excludeString = String.Empty; - string includeString = String.Empty; - string returnValue = String.Empty; - - List azureDevopsFilters = new List(); - - // Replace Azure devops syntax for unit tests to Nunit3 filters - if(!String.IsNullOrWhiteSpace(ExcludeCategory)) - { - azureDevopsFilters.AddRange(ExcludeCategory.Split(new string[] { "--exclude-category" }, StringSplitOptions.None)); - } - - if(!String.IsNullOrWhiteSpace(ExcludeCategory2)) - { - azureDevopsFilters.AddRange(ExcludeCategory2.Split(new string[] { "--exclude-category" }, StringSplitOptions.None)); - } - - for(int i = 0; i < azureDevopsFilters.Count; i++) - { - if(!String.IsNullOrWhiteSpace(excludeString)) - excludeString += " && "; - - excludeString += $" cat != {azureDevopsFilters[i]} "; - } - - String.Join(" cat != ", azureDevopsFilters); - - if(!String.IsNullOrWhiteSpace(IncludeCategory)) - { - foreach (var item in IncludeCategory.Split(new string[] { "--include-category" }, StringSplitOptions.None)) - { - if(!String.IsNullOrWhiteSpace(includeString)) - includeString += " || "; - - includeString += $" cat == {item} "; - } - } - - foreach(var filter in new []{nunitWhere,includeString,excludeString}.Where(x=> !String.IsNullOrWhiteSpace(x))) - { - if(!String.IsNullOrWhiteSpace(returnValue)) - returnValue += " && "; - - returnValue += $"({filter})"; - } - - return returnValue; -} +RunTarget(target); \ No newline at end of file diff --git a/eng/cake/dotnet.cake b/eng/cake/dotnet.cake index cc4feae959c7..433bdf9b34b0 100644 --- a/eng/cake/dotnet.cake +++ b/eng/cake/dotnet.cake @@ -2,6 +2,11 @@ var ext = IsRunningOnWindows() ? ".exe" : ""; var dotnetPath = $"./bin/dotnet/dotnet{ext}"; +string configuration = GetBuildVariable("configuration", GetBuildVariable("BUILD_CONFIGURATION", "DEBUG")); +var localDotnet = GetBuildVariable("workloads", "local") == "local"; +var vsVersion = GetBuildVariable("VS", ""); +string MSBuildExe = Argument("msbuild", EnvironmentVariable("MSBUILD_EXE", "")); +Exception pendingException = null; // Tasks for CI @@ -15,7 +20,7 @@ Task("dotnet") DotNetCoreBuild("./src/DotNet/DotNet.csproj", new DotNetCoreBuildSettings { MSBuildSettings = new DotNetCoreMSBuildSettings() - .EnableBinaryLogger($"{logDirectory}/dotnet-{configuration}.binlog") + .EnableBinaryLogger($"{GetLogDirectory()}/dotnet-{configuration}.binlog") .SetConfiguration(configuration), }); }); @@ -29,7 +34,7 @@ Task("dotnet-local-workloads") DotNetCoreBuild("./src/DotNet/DotNet.csproj", new DotNetCoreBuildSettings { MSBuildSettings = new DotNetCoreMSBuildSettings() - .EnableBinaryLogger($"{logDirectory}/dotnet-{configuration}.binlog") + .EnableBinaryLogger($"{GetLogDirectory()}/dotnet-{configuration}.binlog") .SetConfiguration(configuration) .WithProperty("InstallWorkloadPacks", "false"), }); @@ -37,7 +42,7 @@ Task("dotnet-local-workloads") DotNetCoreBuild("./src/DotNet/DotNet.csproj", new DotNetCoreBuildSettings { MSBuildSettings = new DotNetCoreMSBuildSettings() - .EnableBinaryLogger($"{logDirectory}/dotnet-install-{configuration}.binlog") + .EnableBinaryLogger($"{GetLogDirectory()}/dotnet-install-{configuration}.binlog") .SetConfiguration(configuration) .WithTarget("Install"), ToolPath = dotnetPath, @@ -45,10 +50,21 @@ Task("dotnet-local-workloads") }); Task("dotnet-buildtasks") + .WithCriteria(Argument("sln", null) == null) .IsDependentOn("dotnet") .Does(() => { RunMSBuildWithDotNet("./Microsoft.Maui.BuildTasks.slnf"); + }) + .OnError(exception => + { + if (IsTarget("VS")) + { + pendingException = exception; + return; + } + + throw exception; }); Task("dotnet-build") @@ -80,7 +96,7 @@ Task("dotnet-templates") var dn = localDotnet ? dotnetPath : "dotnet"; - var templatesTest = tempDirectory.Combine("templatesTest"); + var templatesTest = GetTempDirectory().Combine("templatesTest"); EnsureDirectoryExists(templatesTest); CleanDirectories(templatesTest.FullPath); @@ -210,6 +226,7 @@ Task("dotnet-test") }); Task("dotnet-pack-maui") + .WithCriteria(RunPackTarget()) .Does(() => { DotNetCoreTool("pwsh", new DotNetCoreToolSettings @@ -220,6 +237,7 @@ Task("dotnet-pack-maui") }); Task("dotnet-pack-additional") + .WithCriteria(RunPackTarget()) .Does(() => { // Download some additional symbols that need to be archived along with the maui symbols: @@ -241,6 +259,7 @@ Task("dotnet-pack-additional") }); Task("dotnet-pack-library-packs") + .WithCriteria(RunPackTarget()) .Does(() => { var tempDir = $"./artifacts/library-packs-temp"; @@ -270,6 +289,7 @@ Task("dotnet-pack-library-packs") }); Task("dotnet-pack") + .WithCriteria(RunPackTarget()) .IsDependentOn("dotnet-pack-maui") .IsDependentOn("dotnet-pack-additional") .IsDependentOn("dotnet-pack-library-packs"); @@ -291,11 +311,11 @@ Task("dotnet-diff") else { // clean all working folders - var diffCacheDir = tempDirectory.Combine("diffCache"); + var diffCacheDir = GetTempDirectory().Combine("diffCache"); EnsureDirectoryExists(diffCacheDir); CleanDirectories(diffCacheDir.FullPath); - EnsureDirectoryExists(diffDirectory); - CleanDirectories(diffDirectory.FullPath); + EnsureDirectoryExists(GetDiffDirectory()); + CleanDirectories(GetDiffDirectory().FullPath); // run the diff foreach (var nupkg in nupkgs) @@ -311,7 +331,7 @@ Task("dotnet-diff") .Append("--prerelease") .Append("--group-ids") .Append("--ignore-unchanged") - .AppendSwitchQuoted("--output", diffDirectory.FullPath) + .AppendSwitchQuoted("--output", GetDiffDirectory().FullPath) .AppendSwitchQuoted("--cache", diffCacheDir.FullPath) }); } @@ -326,7 +346,7 @@ Task("dotnet-diff") Information("Unable to clean up diff cache directory."); } - var diffs = GetFiles($"{diffDirectory}/**/*.md"); + var diffs = GetFiles($"{GetDiffDirectory()}/**/*.md"); if (!diffs.Any()) { Warning($"##vso[task.logissue type=warning]No NuGet diffs were found."); @@ -366,48 +386,58 @@ Task("dotnet-diff") }); // Tasks for Local Development - -Task("VS-DOGFOOD") - .Description("Provisions .NET 6 and launches an instance of Visual Studio using it.") +Task("VS") + .Description("Provisions .NET 6, and launches an instance of Visual Studio using it.") + .IsDependentOn("Clean") .IsDependentOn("dotnet") + .IsDependentOn("dotnet-buildtasks") + .IsDependentOn("dotnet-pack") // Run conditionally .Does(() => { - StartVisualStudioForDotNet6(null); - }); + if (pendingException != null) + { + Error($"{pendingException}"); + Error("!!!!BUILD TASKS FAILED: !!!!!"); + } + + StartVisualStudioForDotNet6(); + }); +// Keeping this for users that are already using this. Task("VS-NET6") .Description("Provisions .NET 6 and launches an instance of Visual Studio using it.") .IsDependentOn("Clean") - .IsDependentOn("dotnet") - .IsDependentOn("dotnet-buildtasks") + .IsDependentOn("VS") .Does(() => { - StartVisualStudioForDotNet6(); + Warning("!!!!Please switch to using the `VS` target.!!!!"); }); -Task("VS-WINUI") - .Description("Provisions .NET 6 and launches an instance of Visual Studio with WinUI projects.") - .IsDependentOn("VS-NET6"); - // .IsDependentOn("dotnet") WINUI currently can't launch application with local dotnet - // .IsDependentOn("dotnet-buildtasks") +bool RunPackTarget() +{ + // Is the user running the pack target explicitly? + if (TargetStartsWith("dotnet-pack")) + return true; -Task("VS-ANDROID") - .Description("Provisions .NET 6 and launches an instance of Visual Studio with Android projects.") - .IsDependentOn("Clean") - .IsDependentOn("dotnet") - .IsDependentOn("dotnet-buildtasks") - .Does(() => + // If the default target is running then let the pack target run + if (IsTarget("Default")) + return true; + + // Does the user want to run a pack as part of a different target? + if (HasArgument("pack")) + return true; + + // If the request is to open a different sln then let's see if pack has ever run + // if it hasn't then lets pack maui so the sln will open + if (Argument("sln", null) != null) { - DotNetCoreRestore("./Microsoft.Maui.sln", new DotNetCoreRestoreSettings - { - ToolPath = dotnetPath - }); + var mauiPacks = MakeAbsolute(Directory("./bin/dotnet/packs/Microsoft.Maui.Sdk")).ToString(); + if (!DirectoryExists(mauiPacks)) + return true; + } - // VS has trouble building all the references correctly so this makes sure everything is built - // and we're ready to go right when VS launches - RunMSBuildWithDotNet("./src/Controls/samples/Controls.Sample/Maui.Controls.Sample.csproj"); - StartVisualStudioForDotNet6("./Microsoft.Maui.Droid.sln"); - }); + return false; +} string FindMSBuild() { @@ -431,6 +461,30 @@ string FindMSBuild() return "msbuild"; } + +Dictionary GetDotNetEnvironmentVariables() +{ + Dictionary envVariables = new Dictionary(); + var dotnet = MakeAbsolute(Directory("./bin/dotnet/")).ToString(); + + envVariables.Add("DOTNET_INSTALL_DIR", dotnet); + envVariables.Add("DOTNET_ROOT", dotnet); + envVariables.Add("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", dotnet); + envVariables.Add("DOTNET_MULTILEVEL_LOOKUP", "0"); + envVariables.Add("MSBuildEnableWorkloadResolver", "true"); + + var existingPath = EnvironmentVariable("PATH"); + Information(dotnet + ":" + existingPath); + envVariables.Add("PATH", dotnet + ":" + existingPath); + + // Get "full" .binlog in Project System Tools + if (HasArgument("debug")) + envVariables.Add("MSBuildDebugEngine", "1"); + + return envVariables; + +} + void SetDotNetEnvironmentVariables() { var dotnet = MakeAbsolute(Directory("./bin/dotnet/")).ToString(); @@ -441,11 +495,22 @@ void SetDotNetEnvironmentVariables() SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0"); SetEnvironmentVariable("MSBuildEnableWorkloadResolver", "true"); SetEnvironmentVariable("PATH", dotnet, prepend: true); + + // Get "full" .binlog in Project System Tools + if (HasArgument("debug")) + SetEnvironmentVariable("MSBuildDebugEngine", "1"); } -void StartVisualStudioForDotNet6(string sln = null) +void StartVisualStudioForDotNet6() { - if (sln == null) + string sln = Argument("sln", null); + + bool includePrerelease = true; + + if (!String.IsNullOrEmpty(vsVersion)) + includePrerelease = (vsVersion == "preview"); + + if (String.IsNullOrWhiteSpace(sln)) { if (IsRunningOnWindows()) { @@ -453,35 +518,34 @@ void StartVisualStudioForDotNet6(string sln = null) } else { - sln = "./Microsoft.Maui-mac.slnf"; + sln = "_omnisharp.sln"; } } - if (isCIBuild) + + if (IsCIBuild()) { - Information("This target should not run on CI."); + Error("This target should not run on CI."); return; } + if(localDotnet) { SetDotNetEnvironmentVariables(); SetEnvironmentVariable("_ExcludeMauiProjectCapability", "true"); } + if (IsRunningOnWindows()) { - bool includePrerelease = true; - - if (!String.IsNullOrEmpty(vsVersion)) - includePrerelease = (vsVersion == "preview"); - var vsLatest = VSWhereLatest(new VSWhereLatestSettings { IncludePrerelease = includePrerelease, }); if (vsLatest == null) throw new Exception("Unable to find Visual Studio!"); - + StartProcess(vsLatest.CombineWithFilePath("./Common7/IDE/devenv.exe"), sln); } else { - StartProcess("open", new ProcessSettings{ Arguments = sln }); + + StartProcess("open", new ProcessSettings{ Arguments = sln, EnvironmentVariables = GetDotNetEnvironmentVariables() }); } } @@ -501,8 +565,8 @@ void RunMSBuildWithDotNet( var name = System.IO.Path.GetFileNameWithoutExtension(sln); var type = useDotNetBuild ? "dotnet" : "msbuild"; var binlog = string.IsNullOrEmpty(targetFramework) ? - $"\"{logDirectory}/{name}-{configuration}-{target}-{type}.binlog\"" : - $"\"{logDirectory}/{name}-{configuration}-{target}-{targetFramework}-{type}.binlog\""; + $"\"{GetLogDirectory()}/{name}-{configuration}-{target}-{type}.binlog\"" : + $"\"{GetLogDirectory()}/{name}-{configuration}-{target}-{targetFramework}-{type}.binlog\""; if(localDotnet) SetDotNetEnvironmentVariables(); @@ -533,6 +597,7 @@ void RunMSBuildWithDotNet( { MSBuildSettings = msbuildSettings, }; + dotnetBuildSettings.ArgumentCustomization = args => { if (!restore) @@ -588,7 +653,7 @@ void RunMSBuildWithDotNet( void RunTestWithLocalDotNet(string csproj) { var name = System.IO.Path.GetFileNameWithoutExtension(csproj); - var binlog = $"{logDirectory}/{name}-{configuration}.binlog"; + var binlog = $"{GetLogDirectory()}/{name}-{configuration}.binlog"; var results = $"{name}-{configuration}.trx"; if(localDotnet) @@ -601,7 +666,7 @@ void RunTestWithLocalDotNet(string csproj) ToolPath = dotnetPath, NoBuild = true, Logger = $"trx;LogFileName={results}", - ResultsDirectory = testResultsDirectory, + ResultsDirectory = GetTestResultsDirectory(), ArgumentCustomization = args => args.Append($"-bl:{binlog}") }); } diff --git a/eng/cake/helpers.cake b/eng/cake/helpers.cake index 7c4ae849c0b8..628bec7ccfa5 100644 --- a/eng/cake/helpers.cake +++ b/eng/cake/helpers.cake @@ -1,4 +1,7 @@ +bool isCleanSet = HasArgument("clean") || IsTarget("clean"); + Task("Clean") + .WithCriteria(isCleanSet) .Description("Deletes all the obj/bin directories") .Does(() => { @@ -21,7 +24,34 @@ Task("Clean") } }); +DirectoryPath _artifactStagingDirectory; +DirectoryPath GetArtifactStagingDirectory() => + _artifactStagingDirectory ??= MakeAbsolute(Directory(EnvironmentVariable("BUILD_ARTIFACTSTAGINGDIRECTORY", "artifacts"))); + +DirectoryPath _logDirectory; +DirectoryPath GetLogDirectory() => + _logDirectory ??= MakeAbsolute(Directory(EnvironmentVariable("LogDirectory", $"{GetArtifactStagingDirectory()}/logs"))); + +DirectoryPath _testResultsDirectory; +DirectoryPath GetTestResultsDirectory() => + _testResultsDirectory ??= MakeAbsolute(Directory(EnvironmentVariable("TestResultsDirectory", $"{GetArtifactStagingDirectory()}/test-results"))); + +DirectoryPath _diffDirectory; +DirectoryPath GetDiffDirectory() => + _diffDirectory ??= MakeAbsolute(Directory(EnvironmentVariable("ApiDiffDirectory", $"{GetArtifactStagingDirectory()}/api-diff"))); + +DirectoryPath _tempDirectory; +DirectoryPath GetTempDirectory() => + _tempDirectory ??= MakeAbsolute(Directory(EnvironmentVariable("AGENT_TEMPDIRECTORY", EnvironmentVariable("TEMP", EnvironmentVariable("TMPDIR", "../maui-temp")) + "/" + Guid.NewGuid()))); +string GetAgentName() => + EnvironmentVariable("AGENT_NAME", ""); + +bool IsCIBuild() => + !String.IsNullOrWhiteSpace(GetAgentName()); + +bool IsHostedAgent() => + GetAgentName().StartsWith("Azure Pipelines") || GetAgentName().StartsWith("Hosted Agent"); T GetBuildVariable(string key, T defaultValue) { @@ -85,3 +115,9 @@ void SetEnvironmentVariable(string name, string value, bool prepend = false) Information("Setting environment variable: {0} = '{1}'", name, value); } + +bool IsTarget(string target) => + Argument("target", "Default").Equals(target, StringComparison.InvariantCultureIgnoreCase); + +bool TargetStartsWith(string target) => + Argument("target", "Default").StartsWith(target, StringComparison.InvariantCultureIgnoreCase); diff --git a/eng/package.ps1 b/eng/package.ps1 index b672ee334cde..ac0be1097351 100644 --- a/eng/package.ps1 +++ b/eng/package.ps1 @@ -101,12 +101,16 @@ if ($IsWindows) else { $oldPATH=$env:PATH + $oldDOTNET_ROOT=$env:DOTNET_ROOT try { # Put our local dotnet on $PATH $env:PATH=($dotnet + [IO.Path]::PathSeparator + $env:PATH) $dotnet_tool = Join-Path $dotnet dotnet + # This tells .NET to use the bootstrapped runtime + $env:DOTNET_ROOT=$dotnet + # Build with ./bin/dotnet/dotnet & $dotnet_tool pack $slnMac ` -c:$configuration ` @@ -117,5 +121,6 @@ else finally { $env:PATH=$oldPATH + $env:DOTNET_ROOT=$oldDOTNET_ROOT } } diff --git a/src/DotNet/DotNet.csproj b/src/DotNet/DotNet.csproj index c615666631bf..8cc5edff2147 100644 --- a/src/DotNet/DotNet.csproj +++ b/src/DotNet/DotNet.csproj @@ -103,6 +103,14 @@ + + + From efac4b0714c3b7d856315f8dad7c6dcf9fc487fe Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Wed, 18 May 2022 05:16:10 -0600 Subject: [PATCH 12/14] Ensure that the z-index sort also preserves original add order (#7270) Fixes #7122 --- .../src/Handlers/Layout/LayoutExtensions.cs | 37 +++++++++++++++---- .../tests/UnitTests/Layouts/ZIndexTests.cs | 32 ++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/Core/src/Handlers/Layout/LayoutExtensions.cs b/src/Core/src/Handlers/Layout/LayoutExtensions.cs index 500a311708e5..4bcde8f28730 100644 --- a/src/Core/src/Handlers/Layout/LayoutExtensions.cs +++ b/src/Core/src/Handlers/Layout/LayoutExtensions.cs @@ -5,16 +5,25 @@ namespace Microsoft.Maui.Handlers { internal static class LayoutExtensions { - class ZIndexComparer : IComparer + record ViewAndIndex(IView View, int Index); + + class ZIndexComparer : IComparer { - public int Compare(IView? x, IView? y) + public int Compare(ViewAndIndex? x, ViewAndIndex? y) { - if (x == null || y == null) + if(x == null || y == null) { return 0; } - return x.ZIndex.CompareTo(y.ZIndex); + var zIndexCompare = x.View.ZIndex.CompareTo(y.View.ZIndex); + + if (zIndexCompare == 0) + { + return x.Index.CompareTo(y.Index); + } + + return zIndexCompare; } } @@ -22,9 +31,23 @@ public int Compare(IView? x, IView? y) public static IView[] OrderByZIndex(this ILayout layout) { - var ordered = new IView[layout.Count]; - layout.CopyTo(ordered, 0); - Array.Sort(ordered, s_comparer); + var count = layout.Count; + var indexedViews = new ViewAndIndex[count]; + + for (int n = 0; n < count; n++) + { + indexedViews[n] = new ViewAndIndex(layout[n], n); + } + + Array.Sort(indexedViews, s_comparer); + + var ordered = new IView[count]; + + for (int n = 0; n < count; n++) + { + ordered[n] = indexedViews[n].View; + } + return ordered; } diff --git a/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs b/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs index 6bde71bec931..5127df109de6 100644 --- a/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs +++ b/src/Core/tests/UnitTests/Layouts/ZIndexTests.cs @@ -243,5 +243,37 @@ public void ZIndexUpdatePreservesAddOrderForEqualZIndexes() Assert.Equal(view2, zordered[2]); Assert.Equal(view3, zordered[3]); } + + [Fact] + public void ZIndexUpdatePreservesAddOrderLotsOfEqualZIndexes() + { + // This tests the same thing as ZIndexUpdatePreservesAddOrderForEqualZIndexes, + // but for more views - since the sorting algorithm can change when the arrays + // are larger, we were running into situations where layouts with more controls + // were _not_ preserving the Add() order when sorting by z-index. + + var layout = new FakeLayout(); + + int views = 100; + int iterations = 10; + + var toAdd = new IView[views]; + + for (int n = 0; n < views; n++) + { + toAdd[n] = CreateTestView(zIndex: 0); + layout.Add(toAdd[n]); + } + + for (int i = 0; i < iterations; i++) + { + var zordered = layout.OrderByZIndex(); + + for (int n = 0; n < zordered.Length; n++) + { + Assert.Equal(toAdd[n], zordered[n]); + } + } + } } } From b8727a40b18bd12a8707af146e19b8be87e86086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 18 May 2022 18:23:43 +0200 Subject: [PATCH 13/14] [Android] Render Border without set a Stroke (#7247) * Render Border even without Stroke on Android * Added device test --- src/Core/src/Graphics/MauiDrawable.Android.cs | 34 ++++++++++++++++--- .../src/Platform/Android/StrokeExtensions.cs | 2 +- .../src/Platform/Android/ViewExtensions.cs | 2 +- .../Border/BorderHandlerTests.Android.cs | 28 +++++++++++++-- 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/Core/src/Graphics/MauiDrawable.Android.cs b/src/Core/src/Graphics/MauiDrawable.Android.cs index 14c50da17bf2..83591349fb8c 100644 --- a/src/Core/src/Graphics/MauiDrawable.Android.cs +++ b/src/Core/src/Graphics/MauiDrawable.Android.cs @@ -164,17 +164,20 @@ public void SetBorderBrush(GPaint? paint) if (paint is SolidPaint solidPaint) SetBorderBrush(solidPaint); - if (paint is LinearGradientPaint linearGradientPaint) + else if (paint is LinearGradientPaint linearGradientPaint) SetBorderBrush(linearGradientPaint); - if (paint is RadialGradientPaint radialGradientPaint) + else if (paint is RadialGradientPaint radialGradientPaint) SetBorderBrush(radialGradientPaint); - if (paint is ImagePaint imagePaint) + else if (paint is ImagePaint imagePaint) SetBorderBrush(imagePaint); - if (paint is PatternPaint patternPaint) + else if (paint is PatternPaint patternPaint) SetBorderBrush(patternPaint); + + else + SetEmptyBorderBrush(); } public void SetBorderBrush(SolidPaint solidPaint) @@ -218,6 +221,27 @@ public void SetBorderBrush(RadialGradientPaint radialGradientPaint) InvalidateSelf(); } + public void SetEmptyBorderBrush() + { + _invalidatePath = true; + + if (_backgroundColor != null) + { + _borderColor = _backgroundColor.Value; + _stroke = null; + } + else + { + _borderColor = null; + + if (_background != null) + SetBorderBrush(_background); + } + + InitializeBorderIfNeeded(); + InvalidateSelf(); + } + public void SetBorderBrush(ImagePaint imagePaint) { throw new NotImplementedException(); @@ -473,7 +497,7 @@ bool HasBorder() { InitializeBorderIfNeeded(); - return _shape != null && (_stroke != null || _borderColor != null); + return _shape != null; } void InitializeBorderIfNeeded() diff --git a/src/Core/src/Platform/Android/StrokeExtensions.cs b/src/Core/src/Platform/Android/StrokeExtensions.cs index 2a4941edce96..c90e8c6cd9dc 100644 --- a/src/Core/src/Platform/Android/StrokeExtensions.cs +++ b/src/Core/src/Platform/Android/StrokeExtensions.cs @@ -129,7 +129,7 @@ public static void InvalidateBorderStrokeBounds(this AView platformView) internal static void UpdateMauiDrawable(this AView platformView, IBorderStroke border) { - bool hasBorder = border.Shape != null && border.Stroke != null; + bool hasBorder = border.Shape != null; if (!hasBorder) return; diff --git a/src/Core/src/Platform/Android/ViewExtensions.cs b/src/Core/src/Platform/Android/ViewExtensions.cs index 63fd6cc81141..2f841a4a14d6 100644 --- a/src/Core/src/Platform/Android/ViewExtensions.cs +++ b/src/Core/src/Platform/Android/ViewExtensions.cs @@ -160,7 +160,7 @@ public static void SetWindowBackground(this AView view) public static void UpdateBackground(this ContentViewGroup platformView, IBorderStroke border) { - bool hasBorder = border.Shape != null && border.Stroke != null; + bool hasBorder = border.Shape != null; if (hasBorder) platformView.UpdateBorderStroke(border); diff --git a/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Android.cs index 4acdd80b0d60..1b78a594e089 100644 --- a/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Border/BorderHandlerTests.Android.cs @@ -1,12 +1,34 @@ using System; using System.Threading.Tasks; -using Microsoft.Maui.Graphics; -using Microsoft.Maui.Handlers; +using Microsoft.Maui.DeviceTests.Stubs; +using Xunit; namespace Microsoft.Maui.DeviceTests { public partial class BorderHandlerTests { + [Theory(DisplayName = "Border render without Stroke")] + [InlineData(0xFF0000)] + [InlineData(0x00FF00)] + [InlineData(0x0000FF)] + public async Task BorderRenderWithoutStroke(uint color) + { + var expected = Color.FromUint(color); + + var border = new BorderStub() + { + Content = new LabelStub { Text = "Without Stroke", TextColor = Colors.White }, + Shape = new RoundRectangleShapeStub { CornerRadius = new CornerRadius(12) }, + Background = new SolidPaintStub(expected), + Stroke = null, + StrokeThickness = 2, + Height = 100, + Width = 300 + }; + + await ValidateHasColor(border, expected); + } + ContentViewGroup GetNativeBorder(BorderHandler borderHandler) => borderHandler.PlatformView; @@ -20,4 +42,4 @@ Task ValidateHasColor(IBorderView border, Color color, Action action = null) }); } } -} +} \ No newline at end of file From 4b833b0cd5f18eab9f3a679c41822010eddff452 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Wed, 18 May 2022 12:24:16 -0600 Subject: [PATCH 14/14] Propagate layout requests up the tree for Content/Layout Views (#7297) Remove unnecessary SetNeedsLayout call when switching visibility Fixes #6420; Fixes #7217 --- .../Compatibility/Handlers/iOS/FrameRenderer.cs | 6 ++++++ src/Core/src/Platform/iOS/ContentView.cs | 6 ++++++ src/Core/src/Platform/iOS/LayoutView.cs | 7 ++++++- src/Core/src/Platform/iOS/ViewExtensions.cs | 16 ++-------------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/iOS/FrameRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/iOS/FrameRenderer.cs index 4143bf32ff74..e6546fa1ada6 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/iOS/FrameRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/iOS/FrameRenderer.cs @@ -173,6 +173,12 @@ protected override void Dispose(bool disposing) } } + public override void SetNeedsLayout() + { + base.SetNeedsLayout(); + Superview?.SetNeedsLayout(); + } + [Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)] class FrameView : Microsoft.Maui.Platform.ContentView { diff --git a/src/Core/src/Platform/iOS/ContentView.cs b/src/Core/src/Platform/iOS/ContentView.cs index 703497815488..8e867638c2d1 100644 --- a/src/Core/src/Platform/iOS/ContentView.cs +++ b/src/Core/src/Platform/iOS/ContentView.cs @@ -31,6 +31,12 @@ public override void LayoutSubviews() CrossPlatformArrange?.Invoke(bounds); } + public override void SetNeedsLayout() + { + base.SetNeedsLayout(); + Superview?.SetNeedsLayout(); + } + internal Func? CrossPlatformMeasure { get; set; } internal Func? CrossPlatformArrange { get; set; } } diff --git a/src/Core/src/Platform/iOS/LayoutView.cs b/src/Core/src/Platform/iOS/LayoutView.cs index fd6d55fc0574..a09636fe2d21 100644 --- a/src/Core/src/Platform/iOS/LayoutView.cs +++ b/src/Core/src/Platform/iOS/LayoutView.cs @@ -35,11 +35,16 @@ public override void LayoutSubviews() base.LayoutSubviews(); var bounds = AdjustForSafeArea(Bounds).ToRectangle(); - CrossPlatformMeasure?.Invoke(bounds.Width, bounds.Height); CrossPlatformArrange?.Invoke(bounds); } + public override void SetNeedsLayout() + { + base.SetNeedsLayout(); + Superview?.SetNeedsLayout(); + } + public override void SubviewAdded(UIView uiview) { base.SubviewAdded(uiview); diff --git a/src/Core/src/Platform/iOS/ViewExtensions.cs b/src/Core/src/Platform/iOS/ViewExtensions.cs index 8299e1cb192c..b127581b064a 100644 --- a/src/Core/src/Platform/iOS/ViewExtensions.cs +++ b/src/Core/src/Platform/iOS/ViewExtensions.cs @@ -40,33 +40,21 @@ public static void Unfocus(this UIView platformView, IView view) public static void UpdateVisibility(this UIView platformView, Visibility visibility) { - var shouldLayout = false; - switch (visibility) { case Visibility.Visible: - shouldLayout = platformView.Inflate(); + platformView.Inflate(); platformView.Hidden = false; break; case Visibility.Hidden: - shouldLayout = platformView.Inflate(); + platformView.Inflate(); platformView.Hidden = true; break; case Visibility.Collapsed: platformView.Hidden = true; platformView.Collapse(); - shouldLayout = true; break; } - - // If the view is just switching between Visible and Hidden, then a re-layout isn't necessary. The return value - // from Inflate will tell us if the view was previously collapsed. If the view is switching to or from a collapsed - // state, then we'll have to ask for a re-layout. - - if (shouldLayout) - { - platformView.Superview?.SetNeedsLayout(); - } } public static void UpdateBackground(this ContentView platformView, IBorderStroke border)