From 76e9f74d7b3fb723679537ba9bd7da5128f2aaa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Rozs=C3=ADval?= Date: Fri, 16 Feb 2024 20:03:58 +0100 Subject: [PATCH 01/28] [XC] Allow generic types in x:DataType and x:Type (#20625) * Add parser for single type expression * Allow generic types in x:DataType * Allow generic types in x:Type * Add test * Improve test --- .../src/Build.Tasks/SetPropertiesVisitor.cs | 15 ++-- .../src/Build.Tasks/XmlTypeExtensions.cs | 20 ++--- src/Controls/src/Xaml/TypeArgumentsParser.cs | 12 +++ src/Controls/src/Xaml/XamlServiceProvider.cs | 27 +------ .../Xaml.UnitTests/Issues/Maui20616.xaml | 13 ++++ .../Xaml.UnitTests/Issues/Maui20616.xaml.cs | 76 +++++++++++++++++++ 6 files changed, 119 insertions(+), 44 deletions(-) create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml.cs diff --git a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs index 20bde9bd9a47..aa6828c72560 100644 --- a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs +++ b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs @@ -419,12 +419,17 @@ static IEnumerable CompileBindingPath(ElementNode node, ILContext c if (dataType is null) throw new BuildException(XDataTypeSyntax, dataTypeNode as IXmlLineInfo, null); - var prefix = dataType.Contains(":") ? dataType.Substring(0, dataType.IndexOf(":", StringComparison.Ordinal)) : ""; - var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? ""; - if (!string.IsNullOrEmpty(prefix) && string.IsNullOrEmpty(namespaceuri)) + XmlType dtXType = null; + try + { + dtXType = TypeArgumentsParser.ParseSingle(dataType, node.NamespaceResolver, dataTypeNode as IXmlLineInfo) + ?? throw new BuildException(XDataTypeSyntax, dataTypeNode as IXmlLineInfo, null); + } + catch (XamlParseException) + { + var prefix = dataType.Contains(":") ? dataType.Substring(0, dataType.IndexOf(":", StringComparison.Ordinal)) : ""; throw new BuildException(XmlnsUndeclared, dataTypeNode as IXmlLineInfo, null, prefix); - - var dtXType = new XmlType(namespaceuri, dataType, null); + } var tSourceRef = dtXType.GetTypeReference(context.Cache, module, (IXmlLineInfo)node); if (tSourceRef == null) diff --git a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs index 57dfee10f62c..de34074a2742 100644 --- a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs +++ b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs @@ -50,25 +50,17 @@ static IList GatherXmlnsDefinitionAttributes(ModuleDef return xmlnsDefinitions; } - public static TypeReference GetTypeReference(XamlCache cache, string xmlType, ModuleDefinition module, BaseNode node) + public static TypeReference GetTypeReference(XamlCache cache, string typeName, ModuleDefinition module, BaseNode node) { - var split = xmlType.Split(':'); - if (split.Length > 2) - throw new BuildException(BuildExceptionCode.InvalidXaml, node as IXmlLineInfo, null, xmlType); - - string prefix, name; - if (split.Length == 2) + try { - prefix = split[0]; - name = split[1]; + XmlType xmlType = TypeArgumentsParser.ParseSingle(typeName, node.NamespaceResolver, (IXmlLineInfo)node); + return GetTypeReference(xmlType, cache, module, node as IXmlLineInfo); } - else + catch (XamlParseException) { - prefix = ""; - name = split[0]; + throw new BuildException(BuildExceptionCode.InvalidXaml, node as IXmlLineInfo, null, typeName); } - var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? ""; - return GetTypeReference(new XmlType(namespaceuri, name, null), cache, module, node as IXmlLineInfo); } public static TypeReference GetTypeReference(XamlCache cache, string namespaceURI, string typename, ModuleDefinition module, IXmlLineInfo xmlInfo) diff --git a/src/Controls/src/Xaml/TypeArgumentsParser.cs b/src/Controls/src/Xaml/TypeArgumentsParser.cs index 7e730ca6f33d..29859504fe2c 100644 --- a/src/Controls/src/Xaml/TypeArgumentsParser.cs +++ b/src/Controls/src/Xaml/TypeArgumentsParser.cs @@ -18,6 +18,18 @@ public static IList ParseExpression(string expression, IXmlNamespaceRes return typeList; } + public static XmlType ParseSingle(string expression, IXmlNamespaceResolver resolver, IXmlLineInfo lineInfo) + { + string remaining = null; + XmlType type = Parse(expression, ref remaining, resolver, lineInfo); + if (type is null || !string.IsNullOrWhiteSpace(remaining)) + { + throw new XamlParseException($"Invalid type expression or more than one type declared in '{expression}'", lineInfo, null); + } + + return type; + } + static XmlType Parse(string match, ref string remaining, IXmlNamespaceResolver resolver, IXmlLineInfo lineinfo) { remaining = null; diff --git a/src/Controls/src/Xaml/XamlServiceProvider.cs b/src/Controls/src/Xaml/XamlServiceProvider.cs index 23bf38738b32..aa2e0cb42629 100644 --- a/src/Controls/src/Xaml/XamlServiceProvider.cs +++ b/src/Controls/src/Xaml/XamlServiceProvider.cs @@ -199,23 +199,6 @@ internal bool TryResolve(XmlType xmlType, out Type type) Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider, out XamlParseException exception) { - exception = null; - var split = qualifiedTypeName.Split(':'); - if (split.Length > 2) - return null; - - string prefix, name; - if (split.Length == 2) - { - prefix = split[0]; - name = split[1]; - } - else - { - prefix = ""; - name = split[0]; - } - IXmlLineInfo xmlLineInfo = null; if (serviceProvider != null) { @@ -223,14 +206,8 @@ Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider, out Xam xmlLineInfo = lineInfoProvider.XmlLineInfo; } - var namespaceuri = namespaceResolver.LookupNamespace(prefix); - if (namespaceuri == null) - { - exception = new XamlParseException($"No xmlns declaration for prefix \"{prefix}\"", xmlLineInfo); - return null; - } - - return getTypeFromXmlName(new XmlType(namespaceuri, name, null), xmlLineInfo, currentAssembly, out exception); + var xmlType = TypeArgumentsParser.ParseSingle(qualifiedTypeName, namespaceResolver, xmlLineInfo); + return getTypeFromXmlName(xmlType, xmlLineInfo, currentAssembly, out exception); } internal delegate Type GetTypeFromXmlName(XmlType xmlType, IXmlLineInfo xmlInfo, Assembly currentAssembly, out XamlParseException exception); diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml new file mode 100644 index 000000000000..117d7b2c5d60 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml.cs new file mode 100644 index 000000000000..383b69c8e09b --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui20616.xaml.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using Microsoft.Maui.ApplicationModel; +using Microsoft.Maui.Controls.Core.UnitTests; +using Microsoft.Maui.Controls.Shapes; +using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui.Devices; +using Microsoft.Maui.Dispatching; + +using Microsoft.Maui.Graphics; +using Microsoft.Maui.UnitTests; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class Maui20616 +{ + public Maui20616() + { + InitializeComponent(); + BindingContext = new ViewModel20616 { Value = "Foo" }; + } + + public Maui20616(bool useCompiledXaml) + { + //this stub will be replaced at compile time + } + + [TestFixture] + class Test + { + [SetUp] + public void Setup() + { + Application.SetCurrentApplication(new MockApplication()); + DispatcherProvider.SetCurrent(new DispatcherProviderStub()); + } + + [TearDown] public void TearDown() => AppInfo.SetCurrent(null); + + [Test] + public void XDataTypeCanBeGeneric([Values(false, true)] bool useCompiledXaml) + { + var page = new Maui20616(useCompiledXaml); + + page.LabelA.BindingContext = new ViewModel20616 { Value = "ABC" }; + Assert.AreEqual("ABC", page.LabelA.Text); + + if (useCompiledXaml) + { + var binding = page.LabelA.GetContext(Label.TextProperty).Bindings.Values.Single(); + Assert.That(binding, Is.TypeOf, string>>()); + } + + page.LabelB.BindingContext = new ViewModel20616> { Value = new ViewModel20616 { Value = true } }; + Assert.AreEqual("True", page.LabelB.Text); + + if (useCompiledXaml) + { + var binding = page.LabelB.GetContext(Label.TextProperty).Bindings.Values.Single(); + Assert.That(binding, Is.TypeOf>, bool>>()); + } + + Assert.AreEqual(typeof(ViewModel20616), page.Resources["ViewModelBool"]); + Assert.AreEqual(typeof(ViewModel20616>), page.Resources["NestedViewModel"]); + } + } +} + +public class ViewModel20616 +{ + public required T Value { get; init; } +} \ No newline at end of file From 961f00f261b19e417de2bde556737d094673399b Mon Sep 17 00:00:00 2001 From: PavloLukianets <109070173+PavloLukianets@users.noreply.github.com> Date: Fri, 16 Feb 2024 23:30:30 +0200 Subject: [PATCH 02/28] Fix modal page push in immersive mode (#19903) * Fix modal page push in immersive mode * Change GetInsetsIgnoringVisibility to GetInsets * Revert "Fix modal page push in immersive mode" This reverts commit a2526ea2fb86624f1739caafe7acb37c27e6d671. --- .../ModalNavigationManager/ModalNavigationManager.Android.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs index 7b29d8f61c54..21a0341e4be9 100644 --- a/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs +++ b/src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs @@ -327,7 +327,7 @@ void UpdateMargin() var windowInsets = ViewCompat.GetRootWindowInsets(decorView); if (windowInsets is not null) { - var barInsets = windowInsets.GetInsetsIgnoringVisibility(WindowInsetsCompat.Type.SystemBars()); + var barInsets = windowInsets.GetInsets(WindowInsetsCompat.Type.SystemBars()); if (mlp.TopMargin != barInsets.Top) mlp.TopMargin = barInsets.Top; From 7773f909f93dabef282e0f6228081e3f04eafe2a Mon Sep 17 00:00:00 2001 From: Jonathan Dick Date: Sat, 17 Feb 2024 12:18:03 -0500 Subject: [PATCH 03/28] [Android Connectivity] Set app package on Intent used to invoke context receiver for network callback (#20651) * No need to export app specific broadcast receivers We are the only ones invoking these, so there's no need to export. The export made things 'work' on android 14 because of changes to that api level, but that wasn't the most correct fix. * Set package on connectivity intent With changes in android 14, we need to scope the connectivity intent we use to trigger our own internal broadcast receiver to the current app package (which we get from the application context). --- src/Essentials/src/Battery/Battery.android.cs | 10 ++-- .../src/Connectivity/Connectivity.android.cs | 51 +++++++++++++++++-- .../src/Platform/PlatformUtils.android.cs | 5 +- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/Essentials/src/Battery/Battery.android.cs b/src/Essentials/src/Battery/Battery.android.cs index ae62b86ba3a8..c8bb39bf7416 100755 --- a/src/Essentials/src/Battery/Battery.android.cs +++ b/src/Essentials/src/Battery/Battery.android.cs @@ -21,7 +21,7 @@ partial class BatteryImplementation : IBattery void StartEnergySaverListeners() { powerReceiver = new EnergySaverBroadcastReceiver(OnEnergySaverChanged); - PlatformUtils.RegisterBroadcastReceiver(powerReceiver, new IntentFilter(PowerManager.ActionPowerSaveModeChanged), false); + PlatformUtils.RegisterBroadcastReceiver(powerReceiver, new IntentFilter(PowerManager.ActionPowerSaveModeChanged)); } void StopEnergySaverListeners() @@ -52,7 +52,7 @@ void StartBatteryListeners() Permissions.EnsureDeclared(); batteryReceiver = new BatteryBroadcastReceiver(OnBatteryInfoChanged); - PlatformUtils.RegisterBroadcastReceiver(batteryReceiver, new IntentFilter(Intent.ActionBatteryChanged), false); + PlatformUtils.RegisterBroadcastReceiver(batteryReceiver, new IntentFilter(Intent.ActionBatteryChanged)); } void StopBatteryListeners() @@ -76,7 +76,7 @@ public double ChargeLevel Permissions.EnsureDeclared(); using (var filter = new IntentFilter(Intent.ActionBatteryChanged)) - using (var battery = PlatformUtils.RegisterBroadcastReceiver(null, filter, false)) + using (var battery = PlatformUtils.RegisterBroadcastReceiver(null, filter)) { if (battery is null) return -1; // Unknown @@ -99,7 +99,7 @@ public BatteryState State Permissions.EnsureDeclared(); using (var filter = new IntentFilter(Intent.ActionBatteryChanged)) - using (var battery = PlatformUtils.RegisterBroadcastReceiver(null, filter, false)) + using (var battery = PlatformUtils.RegisterBroadcastReceiver(null, filter)) { if (battery is null) return BatteryState.Unknown; @@ -129,7 +129,7 @@ public BatteryPowerSource PowerSource Permissions.EnsureDeclared(); using (var filter = new IntentFilter(Intent.ActionBatteryChanged)) - using (var battery = PlatformUtils.RegisterBroadcastReceiver(null, filter, false)) + using (var battery = PlatformUtils.RegisterBroadcastReceiver(null, filter)) { if (battery is null) return BatteryPowerSource.Unknown; diff --git a/src/Essentials/src/Connectivity/Connectivity.android.cs b/src/Essentials/src/Connectivity/Connectivity.android.cs index 4eaba170f6c6..72b2a9a55401 100644 --- a/src/Essentials/src/Connectivity/Connectivity.android.cs +++ b/src/Essentials/src/Connectivity/Connectivity.android.cs @@ -15,8 +15,7 @@ partial class ConnectivityImplementation : IConnectivity /// Unique identifier for the connectivity changed action on Android. /// public const string ConnectivityChangedAction = "com.maui.essentials.ESSENTIALS_CONNECTIVITY_CHANGED"; - static Intent connectivityIntent = new Intent(ConnectivityChangedAction); - + static ConnectivityManager connectivityManager; static ConnectivityManager ConnectivityManager => @@ -45,13 +44,15 @@ void StartListeners() conectivityReceiver = new ConnectivityBroadcastReceiver(OnConnectivityChanged); - PlatformUtils.RegisterBroadcastReceiver(conectivityReceiver, filter, true); + PlatformUtils.RegisterBroadcastReceiver(conectivityReceiver, filter); } void StopListeners() { if (conectivityReceiver == null) + { return; + } try { @@ -77,11 +78,15 @@ void StopListeners() void RegisterNetworkCallback() { if (!OperatingSystem.IsAndroidVersionAtLeast(24)) + { return; + } var manager = ConnectivityManager; if (manager == null) + { return; + } var request = new NetworkRequest.Builder().Build(); networkCallback = new EssentialsNetworkCallback(); @@ -91,11 +96,15 @@ void RegisterNetworkCallback() void UnregisterNetworkCallback() { if (!OperatingSystem.IsAndroidVersionAtLeast(24)) + { return; + } var manager = ConnectivityManager; if (manager == null || networkCallback == null) + { return; + } manager.UnregisterNetworkCallback(networkCallback); @@ -104,6 +113,14 @@ void UnregisterNetworkCallback() class EssentialsNetworkCallback : ConnectivityManager.NetworkCallback { + readonly Intent connectivityIntent; + + public EssentialsNetworkCallback() + { + connectivityIntent = new Intent(ConnectivityChangedAction); + connectivityIntent.SetPackage(Application.Context.PackageName); + } + public override void OnAvailable(Network network) => Application.Context.SendBroadcast(connectivityIntent); @@ -159,7 +176,9 @@ public NetworkAccess NetworkAccess var capabilities = manager.GetNetworkCapabilities(network); if (capabilities == null) + { continue; + } #pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CA1416 // Validate platform compatibility @@ -167,7 +186,9 @@ public NetworkAccess NetworkAccess var info = manager.GetNetworkInfo(network); if (info == null || !info.IsAvailable) + { continue; + } #pragma warning restore CS0618 // Type or member is obsolete // Check to see if it has the internet capability @@ -200,12 +221,18 @@ void ProcessAllNetworkInfo() void ProcessNetworkInfo(NetworkInfo info) { if (info == null || !info.IsAvailable) + { return; + } if (info.IsConnected) + { currentAccess = IsBetterAccess(currentAccess, NetworkAccess.Internet); + } else if (info.IsConnectedOrConnecting) + { currentAccess = IsBetterAccess(currentAccess, NetworkAccess.ConstrainedInternet); + } #pragma warning restore CA1422 // Validate platform compatibility #pragma warning restore CA1416 // Validate platform compatibility #pragma warning restore CS0618 // Type or member is obsolete @@ -250,7 +277,9 @@ public IEnumerable ConnectionProfiles var p = ProcessNetworkInfo(info); if (p.HasValue) + { yield return p.Value; + } } #pragma warning disable CS0618 // Type or member is obsolete @@ -258,7 +287,9 @@ public IEnumerable ConnectionProfiles { if (info == null || !info.IsAvailable || !info.IsConnectedOrConnecting) + { return null; + } return GetConnectionType(info.Type, info.TypeName); @@ -289,22 +320,34 @@ internal static ConnectionProfile GetConnectionType(ConnectivityType connectivit return ConnectionProfile.Unknown; default: if (string.IsNullOrWhiteSpace(typeName)) + { return ConnectionProfile.Unknown; + } if (typeName.Contains("mobile", StringComparison.OrdinalIgnoreCase)) + { return ConnectionProfile.Cellular; + } if (typeName.Contains("wimax", StringComparison.OrdinalIgnoreCase)) + { return ConnectionProfile.Cellular; + } if (typeName.Contains("wifi", StringComparison.OrdinalIgnoreCase)) + { return ConnectionProfile.WiFi; + } if (typeName.Contains("ethernet", StringComparison.OrdinalIgnoreCase)) + { return ConnectionProfile.Ethernet; + } if (typeName.Contains("bluetooth", StringComparison.OrdinalIgnoreCase)) + { return ConnectionProfile.Bluetooth; + } return ConnectionProfile.Unknown; } @@ -335,7 +378,9 @@ public override async void OnReceive(Context context, Intent intent) #pragma warning disable CS0618 // Type or member is obsolete if (intent.Action != ConnectivityManager.ConnectivityAction && intent.Action != ConnectivityImplementation.ConnectivityChangedAction) #pragma warning restore CS0618 // Type or member is obsolete + { return; + } // await 1500ms to ensure that the the connection manager updates await Task.Delay(1500); diff --git a/src/Essentials/src/Platform/PlatformUtils.android.cs b/src/Essentials/src/Platform/PlatformUtils.android.cs index 3afe2b78e4c8..c37b5609492c 100644 --- a/src/Essentials/src/Platform/PlatformUtils.android.cs +++ b/src/Essentials/src/Platform/PlatformUtils.android.cs @@ -26,13 +26,12 @@ internal static int NextRequestCode() return requestCode; } - internal static Intent? RegisterBroadcastReceiver(BroadcastReceiver? receiver, IntentFilter filter, bool exported) + internal static Intent? RegisterBroadcastReceiver(BroadcastReceiver? receiver, IntentFilter filter) { #if ANDROID34_0_OR_GREATER if (OperatingSystem.IsAndroidVersionAtLeast(34)) { - var flags = exported ? ReceiverFlags.Exported : ReceiverFlags.NotExported; - return Application.Context.RegisterReceiver(receiver, filter, flags); + return Application.Context.RegisterReceiver(receiver, filter, ReceiverFlags.NotExported); } #endif return Application.Context.RegisterReceiver(receiver, filter); From ea9acadde1b378ba96ec24afe45204d3195ac23d Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Mon, 19 Feb 2024 14:35:58 +0100 Subject: [PATCH 04/28] Remove weird comment/copy&paste error (#20693) --- src/Core/src/Animations/Animation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/src/Animations/Animation.cs b/src/Core/src/Animations/Animation.cs index b569917f50d6..a20caed02da1 100644 --- a/src/Core/src/Animations/Animation.cs +++ b/src/Core/src/Animations/Animation.cs @@ -105,7 +105,7 @@ public Animation(List animations) /// public bool HasFinished { get; protected set; } - /// dotnet_analyzer_diagnostic.CA1805.severity = none + /// /// Specifies whether this animation should repeat. /// public bool Repeats { get; set; } @@ -382,4 +382,4 @@ internal virtual void ForceFinish() } } } -} \ No newline at end of file +} From 6674c21e5b53d2d96c60d3d79189d5b2b90db02f Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Mon, 19 Feb 2024 15:12:53 +0100 Subject: [PATCH 05/28] Remove dotnet-format run for net7 branch (#20702) I think this comes from a time where our branching strategy was still different and we needed this. .NET 7 is old now so this can be removed. I don't think we need to have it for net8/9, again because our strategy is different? Let's see if we still need it when we somehow find ourselves in that situation. --- .../workflows/dotnet-format-daily-net7.yml | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 .github/workflows/dotnet-format-daily-net7.yml diff --git a/.github/workflows/dotnet-format-daily-net7.yml b/.github/workflows/dotnet-format-daily-net7.yml deleted file mode 100644 index 48d6886c66b9..000000000000 --- a/.github/workflows/dotnet-format-daily-net7.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Daily code format check net7.0 branch -on: - workflow_dispatch: - schedule: - - cron: 0 0 * * * # Every day at midnight (UTC) - -permissions: - pull-requests: write - contents: write - -jobs: - dotnet-format: - runs-on: windows-latest - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - ref: net7.0 - - - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v3.2.0 - with: - dotnet-version: 7.0.304 - - - name: Run dotnet format - run: dotnet format .\Microsoft.Maui.sln --no-restore --exclude Templates/src BlazorWebView/src/SharedSource/BlazorWebViewDeveloperTools.cs BlazorWebView/src/SharedSource/BlazorWebViewServiceCollectionExtensions.cs Graphics/src/Graphics.Win2D/W2DCanvas.cs Graphics/src/Graphics.Win2D/W2DExtensions.cs - - - name: Commit files - if: steps.format.outputs.has-changes == 'true' - run: | - git config --local user.name "github-actions[bot]" - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - git commit -a -m 'Automated dotnet-format update' - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 - with: - title: '[housekeeping] Automated PR to fix formatting errors' - body: | - Automated PR to fix formatting errors - committer: GitHub - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - labels: | - t/housekeeping ♻︎ - area/infrastructure 🏗️ - assignees: rmarinho, jsuarezruiz - reviewers: rmarinho, jsuarezruiz - branch: housekeeping/fix-codeformatting-net7.0 - base: net7.0 From ffc20bfb8c89b3f4abe3abbed05e3e58fc300551 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Mon, 19 Feb 2024 14:19:07 +0000 Subject: [PATCH 06/28] =?UTF-8?q?[iOS]=C2=A0Figure=20a=20better=20Estimate?= =?UTF-8?q?dItemSize=20for=20HorizontalList=20(#20022)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Samples] Add repro case for issue #15815 * [iOS] Remove dead code * [iOS] Try find a better EstimatedItemSize for horizontal list * [uitest] Add Uitest for CollectionView * Fix test * Pink color --- .../Issues/Issues15815.xaml | 21 +++++++ .../Issues/Issues15815.xaml.cs | 34 ++++++++++++ .../Handlers/Items/iOS/ItemsViewController.cs | 12 +++- .../Handlers/Items/iOS/ItemsViewDelegator.cs | 14 ----- .../Handlers/Items/iOS/ItemsViewLayout.cs | 55 +++++++++++++++++++ .../tests/UITests/Tests/Issues/Issue15815.cs | 21 +++++++ 6 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml create mode 100644 src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml.cs create mode 100644 src/Controls/tests/UITests/Tests/Issues/Issue15815.cs diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml b/src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml new file mode 100644 index 000000000000..c6c614f7ce58 --- /dev/null +++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml.cs b/src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml.cs new file mode 100644 index 000000000000..9c72998d012c --- /dev/null +++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issues15815.xaml.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.ObjectModel; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Xaml; +using Microsoft.Maui.Graphics; + +namespace Maui.Controls.Sample.Issues +{ + + [XamlCompilation(XamlCompilationOptions.Compile)] + [Issue(IssueTracker.Github, 15815, "Horizontal CollectionView does not show the last element under some condition", PlatformAffected.iOS)] + public partial class Issues15815 : ContentPage + { + public Issues15815() + { + InitializeComponent(); + col.ItemsSource = new ObservableCollection + { + new ItemViewModel { Index = 0, Color = Colors.Red, Width = 50 }, + new ItemViewModel { Index = 1, Color = Colors.Green, Width = 100 }, + new ItemViewModel { Index = 2, Color = Colors.Blue, Width = 100 }, + }; + } + + class ItemViewModel + { + public Color Color { get; set; } + public int Width { get; set; } + public int Index { get; set; } + public string Id => $"id-{Index}"; + public string Name => $"Item {Index}"; + } + } +} \ No newline at end of file diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs index 43fc156726d1..420baed2a5b5 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs @@ -35,6 +35,9 @@ public abstract class ItemsViewController : UICollectionViewControll [UnconditionalSuppressMessage("Memory", "MEM0002", Justification = "Proven safe in test: MemoryTests.HandlerDoesNotLeak")] Func _getPrototype; + + [UnconditionalSuppressMessage("Memory", "MEM0002", Justification = "Proven safe in test: MemoryTests.HandlerDoesNotLeak")] + Func _getPrototypeForIndexPath; CGSize _previousContentSize = CGSize.Empty; [UnconditionalSuppressMessage("Memory", "MEM0002", Justification = "Proven safe in test: MemoryTests.HandlerDoesNotLeak")] @@ -236,7 +239,6 @@ void InvalidateMeasureIfContentSizeChanged() (ItemsView as IView)?.InvalidateMeasure(); } } - _previousContentSize = contentSize.Value; } @@ -269,6 +271,9 @@ void EnsureLayoutInitialized() _getPrototype ??= GetPrototype; ItemsViewLayout.GetPrototype = _getPrototype; + _getPrototypeForIndexPath ??= GetPrototypeForIndexPath; + ItemsViewLayout.GetPrototypeForIndexPath = _getPrototypeForIndexPath; + Delegator = CreateDelegator(); CollectionView.Delegate = Delegator; @@ -476,6 +481,11 @@ UICollectionViewCell GetPrototype() var indexPath = NSIndexPath.Create(group, 0); + return GetPrototypeForIndexPath(indexPath); + } + + internal UICollectionViewCell GetPrototypeForIndexPath(NSIndexPath indexPath) + { return CreateMeasurementCell(indexPath); } diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs index 3716b1b87dd8..49d0124ea87e 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs @@ -123,20 +123,6 @@ public override void CellDisplayingEnded(UICollectionView collectionView, UIColl templatedCell.Unbind(); } } - - if (ItemsViewLayout.ScrollDirection == UICollectionViewScrollDirection.Horizontal) - { - var actualWidth = collectionView.ContentSize.Width - collectionView.Bounds.Size.Width; - if (collectionView.ContentOffset.X >= actualWidth || collectionView.ContentOffset.X < 0) - return; - } - else - { - var actualHeight = collectionView.ContentSize.Height - collectionView.Bounds.Size.Height; - - if (collectionView.ContentOffset.Y >= actualHeight || collectionView.ContentOffset.Y < 0) - return; - } } protected virtual (bool VisibleItems, NSIndexPath First, NSIndexPath Center, NSIndexPath Last) GetVisibleItemsIndexPath() diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs index cf7af6be1963..b8275140b167 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs @@ -19,6 +19,8 @@ public abstract class ItemsViewLayout : UICollectionViewFlowLayout CGSize _currentSize; WeakReference> _getPrototype; + WeakReference> _getPrototypeForIndexPath; + readonly Dictionary _cellSizeCache = new(); public ItemsUpdatingScrollMode ItemsUpdatingScrollMode { get; set; } @@ -31,6 +33,12 @@ public Func GetPrototype set => _getPrototype = new(value); } + internal Func GetPrototypeForIndexPath + { + get => _getPrototypeForIndexPath is not null && _getPrototypeForIndexPath.TryGetTarget(out var func) ? func : null; + set => _getPrototypeForIndexPath = new(value); + } + internal ItemSizingStrategy ItemSizingStrategy { get; private set; } protected ItemsViewLayout(ItemsLayout itemsLayout, ItemSizingStrategy itemSizingStrategy = ItemSizingStrategy.MeasureFirstItem) @@ -243,6 +251,7 @@ protected void DetermineCellSize() else { // Autolayout is now enabled, and this is the size used to guess scrollbar size and progress + measure = TryFindEstimatedSize(measure); EstimatedItemSize = measure; } } @@ -595,5 +604,51 @@ internal void ClearCellSizeCache() { _cellSizeCache.Clear(); } + + CGSize TryFindEstimatedSize(CGSize existingMeasurement) + { + if (CollectionView == null || GetPrototypeForIndexPath == null) + return existingMeasurement; + + //Since this issue only seems to be reproducible on Horizontal scrolling, we only check for that + if (ScrollDirection == UICollectionViewScrollDirection.Horizontal) + { + return FindEstimatedSizeUsingWidth(existingMeasurement); + } + + return existingMeasurement; + } + + CGSize FindEstimatedSizeUsingWidth(CGSize existingMeasurement) + { + // TODO: Handle grouping + var group = 0; + var collectionViewWidth = CollectionView.Bounds.Width; + var numberOfItemsInGroup = CollectionView.NumberOfItemsInSection(group); + + // Calculate the number of cells that can fit in the viewport + var numberOfCellsToCheck = Math.Min((int)(collectionViewWidth / existingMeasurement.Width) + 1, numberOfItemsInGroup); + + // Iterate through the cells and find the one with a wider width + for (int i = 1; i < numberOfCellsToCheck; i++) + { + var indexPath = NSIndexPath.Create(group, i); + if (GetPrototypeForIndexPath(indexPath) is ItemsViewCell cellAtIndex) + { + cellAtIndex.ConstrainTo(ConstrainedDimension); + var measureCellAtIndex = cellAtIndex.Measure(); + + // Check if the cell has a wider width + if (measureCellAtIndex.Width > existingMeasurement.Width) + { + existingMeasurement = measureCellAtIndex; + } + + // TODO: Cache this cell size + } + } + + return existingMeasurement; + } } } diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue15815.cs b/src/Controls/tests/UITests/Tests/Issues/Issue15815.cs new file mode 100644 index 000000000000..ddea6591b420 --- /dev/null +++ b/src/Controls/tests/UITests/Tests/Issues/Issue15815.cs @@ -0,0 +1,21 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.AppiumTests.Issues; + +public class Issue15815 : _IssuesUITest +{ + public Issue15815(TestDevice device) + : base(device) + { } + + public override string Issue => "Horizontal CollectionView does not show the last element under some condition"; + + [Test] + public void LastItemIsVisilbe() + { + var lastItem = App.WaitForElement("id-2"); + Assert.AreEqual("Item 2", lastItem.GetText()); + } +} From 89c264619a3965fe614e6ef378d11dd331340502 Mon Sep 17 00:00:00 2001 From: Tim Miller Date: Tue, 20 Feb 2024 02:41:38 +0900 Subject: [PATCH 07/28] Fix Selectors for Menus (#20699) --- src/Core/src/Platform/iOS/MauiUIApplicationDelegate.Menu.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.Menu.cs b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.Menu.cs index df3353f95434..588c27ea5113 100644 --- a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.Menu.cs +++ b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.Menu.cs @@ -84,7 +84,9 @@ public override bool CanPerform(Selector action, NSObject? withSender) [SupportedOSPlatform("ios13.0")] [Export(KeyboardAcceleratorExtensions.MenuItemSelectedSelector)] - static internal void MenuItemSelected(UICommand uiCommand) + #pragma warning disable CA1822 // Selectors can't be static, or else it won't be found + internal void MenuItemSelected(UICommand uiCommand) + #pragma warning restore CA1822 { uiCommand.SendClicked(); } From db94a9b3835ffe56110eccaf79d9bd18e07ad660 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Mon, 19 Feb 2024 12:10:16 -0600 Subject: [PATCH 08/28] Move keyboard device tests to appium (#20670) --- .../Issues/Issue5724.cs | 95 +++++++++++++++++++ .../tests/UITests/Tests/Issues/Issue5724.cs | 57 +++++++++++ .../Handlers/Entry/EntryHandlerTests.cs | 89 ----------------- .../src/UITest.Appium/HelperExtensions.cs | 21 ++++ 4 files changed, 173 insertions(+), 89 deletions(-) create mode 100644 src/Controls/samples/Controls.Sample.UITests/Issues/Issue5724.cs create mode 100644 src/Controls/tests/UITests/Tests/Issues/Issue5724.cs diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issue5724.cs b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue5724.cs new file mode 100644 index 000000000000..a7ef6fb313cd --- /dev/null +++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue5724.cs @@ -0,0 +1,95 @@ +using System.Threading; +using Microsoft.Maui; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Platform; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 5724, "Next Moves To Next Entry and Done Closes Input View", PlatformAffected.Android)] + public class Issue5724 : TestContentPage + { + protected override void Init() + { + var layout = new VerticalStackLayout(); + + var entry1 = new Entry + { + Text = "Entry 1", + ReturnType = ReturnType.Next, + AutomationId = "Entry1" + }; + + entry1.Focused += async (_, _) => + { + // Make sure keyboard opens + await entry1.ShowSoftInputAsync(CancellationToken.None); + }; + + var entry2 = new Entry + { + Text = "Entry 2", + ReturnType = ReturnType.Next, + AutomationId = "Entry2" + }; + + layout.Add(entry1); + layout.Add(entry2); + + var entry3 = new Entry() + { + Text = "Entry Done", + ReturnType = ReturnType.Done, + AutomationId = "EntryDone" + }; + + entry3.Focused += async (_, _) => + { + // Make sure keyboard opens + await entry3.ShowSoftInputAsync(CancellationToken.None); + }; + + layout.Add(entry3); + + layout.Add(new Entry() + { + Text = "Entry Done", + ReturnType = ReturnType.Done, + AutomationId = "EntryDone2" + }); + + layout.Add(new Button() + { + Text = "Send Next Button", + AutomationId = "SendNext", + Command = new Command(() => + { +#if ANDROID + Handler.MauiContext.Context + .GetActivity() + .CurrentFocus + ?.OnCreateInputConnection(new Android.Views.InputMethods.EditorInfo()) + ?.PerformEditorAction(Android.Views.InputMethods.ImeAction.Next); +#endif + }) + }); + + layout.Add(new Button() + { + Text = "Send Done Button", + AutomationId = "SendDone", + Command = new Command(() => + { +#if ANDROID + Handler.MauiContext.Context + .GetActivity() + .CurrentFocus + ?.OnCreateInputConnection(new Android.Views.InputMethods.EditorInfo()) + ?.PerformEditorAction(Android.Views.InputMethods.ImeAction.Done); +#endif + }) + }); + + Content = layout; + } + } +} diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue5724.cs b/src/Controls/tests/UITests/Tests/Issues/Issue5724.cs new file mode 100644 index 000000000000..4705168bed63 --- /dev/null +++ b/src/Controls/tests/UITests/Tests/Issues/Issue5724.cs @@ -0,0 +1,57 @@ +using NUnit.Framework; +using OpenQA.Selenium.Appium.Android; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.AppiumTests.Issues +{ + public class Issue5724 : _IssuesUITest + { + public Issue5724(TestDevice device) : base(device) + { + } + + public override string Issue => "Next Moves To Next Entry and Done Closes Input View"; + + [Test] + public async Task TappingNextMovesToNextElement() + { + this.IgnoreIfPlatforms(new[] + { + TestDevice.Mac, + TestDevice.iOS, + TestDevice.Windows, + }, "Send Keys only works on Android which is why we are ignoring these other platforms"); + + App.WaitForElement("Entry1"); + App.Click("Entry1"); + + await Task.Yield(); + + App.Click("SendNext"); + await Task.Yield(); + + Assert.True(App.IsFocused("Entry2")); + } + + [Test] + public async Task TappingDoneClosesKeyboard() + { + this.IgnoreIfPlatforms(new[] + { + TestDevice.Mac, + TestDevice.iOS, + TestDevice.Windows, + }, "Send Keys only works on Android which is why we are ignoring these other platforms"); + + App.WaitForElement("EntryDone"); + App.Click("EntryDone"); + + await Task.Yield(); + Assert.True(App.IsKeyboardShown()); + App.Click("SendDone"); + await Task.Yield(); + Assert.False(App.IsKeyboardShown()); + } + } +} diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index 0385fed0a0c9..dd6324880389 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -675,95 +675,6 @@ public async Task VerticalTextAlignmentInitializesCorrectly(TextAlignment textAl Assert.Equal(platformAlignment, values.PlatformViewValue); } -#if ANDROID - [Fact] - public async Task NextMovesToNextEntrySuccessfully() - { - EnsureHandlerCreated(builder => - { - builder.ConfigureMauiHandlers(handler => - { - handler.AddHandler(); - handler.AddHandler(); - }); - }); - - var layout = new VerticalStackLayoutStub(); - - var entry1 = new EntryStub - { - Text = "Entry 1", - ReturnType = ReturnType.Next - }; - - var entry2 = new EntryStub - { - Text = "Entry 2", - ReturnType = ReturnType.Next - }; - - layout.Add(entry1); - layout.Add(entry2); - - layout.Width = 100; - layout.Height = 150; - - await InvokeOnMainThreadAsync(async () => - { - var contentViewHandler = CreateHandler(layout); - await contentViewHandler.PlatformView.AttachAndRun(async () => - { - await entry1.SendKeyboardReturnType(ReturnType.Next); - await entry2.WaitForFocused(); - Assert.True(entry2.IsFocused); - }); - }); - } - - [Fact(Skip = "https://github.com/dotnet/maui/issues/20533")] - public async Task DoneClosesKeyboard() - { - EnsureHandlerCreated(builder => - { - builder.ConfigureMauiHandlers(handler => - { - handler.AddHandler(); - handler.AddHandler(); - }); - }); - - var layout = new VerticalStackLayoutStub(); - - var entry1 = new EntryStub - { - Text = "Entry 1", - ReturnType = ReturnType.Done - }; - - var entry2 = new EntryStub - { - Text = "Entry 2", - ReturnType = ReturnType.Done - }; - - layout.Add(entry1); - layout.Add(entry2); - - layout.Width = 100; - layout.Height = 150; - - await InvokeOnMainThreadAsync(async () => - { - var handler = CreateHandler(layout); - await handler.PlatformView.AttachAndRun(async () => - { - await entry1.SendKeyboardReturnType(ReturnType.Done); - await entry1.WaitForKeyboardToHide(); - }); - }); - } -#endif - [Category(TestCategory.Entry)] public class EntryTextStyleTests : TextStyleHandlerTests { diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs index a274bfadcdc8..f195477c1a94 100644 --- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs +++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs @@ -541,6 +541,27 @@ public static void Back(this IApp app) app.CommandExecutor.Execute("back", ImmutableDictionary.Empty); } + + /// + /// Check if element has focused + /// + /// Represents the main gateway to interact with an app. + /// Target element + /// Returns if focused + /// + public static bool IsFocused(this IApp app, string id) + { + if (app is not AppiumApp aaa) + { + throw new InvalidOperationException($"IsFocused is only supported on AppiumApp"); + } + + var activeElement = aaa.Driver.SwitchTo().ActiveElement(); + var element = (AppiumDriverElement)app.WaitForElement(id); + + return element.AppiumElement.Equals(activeElement); + } + static IUIElement Wait(Func query, Func satisfactory, string? timeoutMessage = null, From 3d012bf3afe047af95e5885dfa689a209820873d Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:13:05 +0000 Subject: [PATCH 09/28] Update dependencies from https://github.com/dotnet/xharness build 20240212.4 (#20701) Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.24112.1 -> To Version 9.0.0-prerelease.24112.4 Co-authored-by: dotnet-maestro[bot] --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 944f3c5f4d93..4e5fdf6a92f0 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -21,7 +21,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "9.0.0-prerelease.24112.1", + "version": "9.0.0-prerelease.24112.4", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 39551719d29b..45342747e236 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,16 +1,16 @@ - + https://github.com/dotnet/xharness - 2f3f51a1adec18475563c8a49fd9b668ae9f2f31 + 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 - + https://github.com/dotnet/xharness - 2f3f51a1adec18475563c8a49fd9b668ae9f2f31 + 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 - + https://github.com/dotnet/xharness - 2f3f51a1adec18475563c8a49fd9b668ae9f2f31 + 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 diff --git a/eng/Versions.props b/eng/Versions.props index 741e1f7258e7..e1661aa8687c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -78,9 +78,9 @@ <_HarfBuzzSharpVersion>7.3.0 <_SkiaSharpNativeAssetsVersion>0.0.0-commit.e2c5c86249621857107c779af0f79b4d06613766.655 7.0.114 - 9.0.0-prerelease.24112.1 - 9.0.0-prerelease.24112.1 - 9.0.0-prerelease.24112.1 + 9.0.0-prerelease.24112.4 + 9.0.0-prerelease.24112.4 + 9.0.0-prerelease.24112.4 0.9.2 0.5.13 1.2.0 From 9c7dc888f7dc18ff7d6655415ba72771f9fdd12a Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Mon, 19 Feb 2024 19:29:47 +0000 Subject: [PATCH 10/28] =?UTF-8?q?[main]=C2=A0Update=20to=20stable=20iOS=20?= =?UTF-8?q?and=20Android=20workloads=20(#20576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Sdk and stable iOS and Android workloads * [ci] Don't provision mono * Rollback to 8.0.100 --- eng/Versions.props | 10 +++++----- eng/provisioning/provisioning.csx | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index e1661aa8687c..baa03e792fb1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -23,12 +23,12 @@ 8.0.0 8.0.0 - 34.0.52 + 34.0.79 - 17.2.8004 - 14.2.8004 - 17.2.8004 - 17.2.8004 + 17.2.8022 + 14.2.8022 + 17.2.8022 + 17.2.8022 8.0.130 diff --git a/eng/provisioning/provisioning.csx b/eng/provisioning/provisioning.csx index ac26837aa518..f98455b9c639 100644 --- a/eng/provisioning/provisioning.csx +++ b/eng/provisioning/provisioning.csx @@ -2,7 +2,6 @@ if (IsMac) { ForceJavaCleanup(); MicrosoftOpenJdk ("11.0.13.8.1"); - Item("https://download.mono-project.com/archive/6.12.0/macos-10-universal/MonoFramework-MDK-6.12.0.199.macos10.xamarin.universal.pkg"); AppleCodesignIdentity("Apple Development: Jonathan Dick (FJL7285DY2)", "https://dl.internalx.com/qa/code-signing-entitlements/components-mac-ios-certificate.p12"); AppleCodesignProfile("https://dl.internalx.com/qa/code-signing-entitlements/components-ios-provisioning.mobileprovision"); AppleCodesignProfile("https://dl.internalx.com/qa/code-signing-entitlements/components-mac-provisioning.mobileprovision"); From 464d9fa1ceb97cfb3e080475dcedd6eb80075ad8 Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Wed, 21 Feb 2024 15:07:23 +0100 Subject: [PATCH 11/28] [X] Traverse ListNode when finding DataType (#20742) - fix #20633 --- src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs index aa6828c72560..eea32f2ba5c2 100644 --- a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs +++ b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs @@ -386,7 +386,10 @@ static IEnumerable CompileBindingPath(ElementNode node, ILContext c { if (n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode)) break; - n = n.Parent as IElementNode; + if (n.Parent is ListNode listNode) + n = listNode.Parent as IElementNode; + else + n = n.Parent as IElementNode; } if (dataTypeNode is null) From 8d517af28786ef7e3fa31bb837e542e3e5efe02e Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 21 Feb 2024 11:47:13 -0600 Subject: [PATCH 12/28] Setup Android App With More Accurate settings (#20672) * Setup Android App With More Accurate settings * - fix iOS reset * - fix windows * Update HelperExtensions.cs --- .../Platforms/Android/MainActivity.cs | 2 + src/Controls/tests/UITests/UITest.cs | 6 +++ .../Actions/AppiumLifecycleActions.cs | 36 +++++++++++++- .../src/UITest.Appium/AppiumAndroidApp.cs | 7 ++- src/TestUtils/src/UITest.Appium/AppiumApp.cs | 3 ++ .../src/UITest.Appium/HelperExtensions.cs | 48 ++++++++++++++++++- .../src/UITest.NUnit/UITestContextBase.cs | 2 +- 7 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/Controls/samples/Controls.Sample.UITests/Platforms/Android/MainActivity.cs b/src/Controls/samples/Controls.Sample.UITests/Platforms/Android/MainActivity.cs index be015c14658d..a0eb1ce025ec 100644 --- a/src/Controls/samples/Controls.Sample.UITests/Platforms/Android/MainActivity.cs +++ b/src/Controls/samples/Controls.Sample.UITests/Platforms/Android/MainActivity.cs @@ -1,5 +1,6 @@ using Android.App; using Android.Content.PM; +using Android.Runtime; using Microsoft.Maui; namespace Maui.Controls.Sample.Platform @@ -11,6 +12,7 @@ namespace Maui.Controls.Sample.Platform [IntentFilter( new[] { Microsoft.Maui.ApplicationModel.Platform.Intent.ActionAppAction }, Categories = new[] { Android.Content.Intent.CategoryDefault })] + [Register("com.microsoft.maui.uitests.MainActivity")] public class MainActivity : MauiAppCompatActivity { } diff --git a/src/Controls/tests/UITests/UITest.cs b/src/Controls/tests/UITests/UITest.cs index e1b26e51ad68..8df106671355 100644 --- a/src/Controls/tests/UITests/UITest.cs +++ b/src/Controls/tests/UITests/UITest.cs @@ -1,5 +1,6 @@ using System.Reflection; using NUnit.Framework; +using UITest.Appium; using UITest.Appium.NUnit; using UITest.Core; using VisualTestUtils; @@ -85,6 +86,11 @@ public override IConfig GetTestConfig() return config; } + public override void Reset() + { + App.ResetApp(); + } + public void VerifyScreenshot(string? name = null) { string deviceName = GetTestConfig().GetProperty("DeviceName") ?? string.Empty; diff --git a/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs b/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs index ec0dd0f03593..d84b5e1d2d27 100644 --- a/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs +++ b/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs @@ -1,4 +1,5 @@ -using UITest.Core; +using OpenQA.Selenium.Appium.Android; +using UITest.Core; namespace UITest.Appium { @@ -6,6 +7,7 @@ public class AppiumLifecycleActions : ICommandExecutionGroup { const string LaunchAppCommand = "launchApp"; const string BackgroundAppCommand = "backgroundApp"; + const string ForegroundAppCommand = "foregroundApp"; const string ResetAppCommand = "resetApp"; const string CloseAppCommand = "closeApp"; const string BackCommand = "back"; @@ -15,6 +17,7 @@ public class AppiumLifecycleActions : ICommandExecutionGroup readonly List _commands = new() { LaunchAppCommand, + ForegroundAppCommand, BackgroundAppCommand, ResetAppCommand, CloseAppCommand, @@ -36,6 +39,7 @@ public CommandResponse Execute(string commandName, IDictionary p return commandName switch { LaunchAppCommand => LaunchApp(parameters), + ForegroundAppCommand => ForegroundApp(parameters), BackgroundAppCommand => BackgroundApp(parameters), ResetAppCommand => ResetApp(parameters), CloseAppCommand => CloseApp(parameters), @@ -54,6 +58,16 @@ CommandResponse LaunchApp(IDictionary parameters) return CommandResponse.SuccessEmptyResponse; } + CommandResponse ForegroundApp(IDictionary parameters) + { + if (_app?.Driver is null) + return CommandResponse.FailedEmptyResponse; + + _app.Driver.ActivateApp(_app.GetAppId()); + + return CommandResponse.SuccessEmptyResponse; + } + CommandResponse BackgroundApp(IDictionary parameters) { if (_app?.Driver is null) @@ -69,7 +83,25 @@ CommandResponse ResetApp(IDictionary parameters) if (_app?.Driver is null) return CommandResponse.FailedEmptyResponse; - _app.Driver.ResetApp(); + // Terminate App not supported on Mac + if (_app.GetTestDevice() == TestDevice.Mac) + { + _app.Driver.ResetApp(); + } + else if (_app.GetTestDevice() == TestDevice.Windows) + { + CloseApp(parameters); + _app.Driver.LaunchApp(); + } + else + { + _app.Driver.TerminateApp(_app.GetAppId()); + + if (_app.GetTestDevice() == TestDevice.iOS) + _app.Driver.ActivateApp(_app.GetAppId()); + else + _app.Driver.LaunchApp(); + } return CommandResponse.SuccessEmptyResponse; } diff --git a/src/TestUtils/src/UITest.Appium/AppiumAndroidApp.cs b/src/TestUtils/src/UITest.Appium/AppiumAndroidApp.cs index 0ecfa53ae558..acaae78fb79c 100644 --- a/src/TestUtils/src/UITest.Appium/AppiumAndroidApp.cs +++ b/src/TestUtils/src/UITest.Appium/AppiumAndroidApp.cs @@ -71,14 +71,17 @@ private static AppiumOptions GetOptions(IConfig config) { config.SetProperty("PlatformName", "Android"); config.SetProperty("AutomationName", "UIAutomator2"); + var appId = config.GetProperty("AppId"); var options = new AppiumOptions(); + SetGeneralAppiumOptions(config, options); - var appId = config.GetProperty("AppId"); if (!string.IsNullOrWhiteSpace(appId)) { - options.AddAdditionalAppiumOption(IOSMobileCapabilityType.BundleId, appId); + options.AddAdditionalAppiumOption(MobileCapabilityType.NoReset, "true"); + options.AddAdditionalAppiumOption(AndroidMobileCapabilityType.AppPackage, appId); + options.AddAdditionalAppiumOption(AndroidMobileCapabilityType.AppActivity, $"{appId}.MainActivity"); } return options; diff --git a/src/TestUtils/src/UITest.Appium/AppiumApp.cs b/src/TestUtils/src/UITest.Appium/AppiumApp.cs index 950cae363e8c..ad7c8c6bdcde 100644 --- a/src/TestUtils/src/UITest.Appium/AppiumApp.cs +++ b/src/TestUtils/src/UITest.Appium/AppiumApp.cs @@ -113,6 +113,9 @@ protected static void SetGeneralAppiumOptions(IConfig config, AppiumOptions appi if (config.GetProperty("FullReset")) appiumOptions.AddAdditionalAppiumOption(MobileCapabilityType.FullReset, "true"); + if (config.GetProperty("NoReset")) + appiumOptions.AddAdditionalAppiumOption(MobileCapabilityType.NoReset, "true"); + var appPath = config.GetProperty("AppPath"); if (!string.IsNullOrEmpty(appPath)) appiumOptions.App = appPath; diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs index f195477c1a94..37f9663f0719 100644 --- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs +++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs @@ -95,7 +95,7 @@ public static void SendKeys(this IApp app, int keyCode, int metastate = 0) ske.PressKeyCode(keyCode, metastate); return; } - + throw new InvalidOperationException($"SendKeys is not supported on {aaa.Driver}"); } @@ -452,6 +452,15 @@ public static void BackgroundApp(this IApp app) app.CommandExecutor.Execute("backgroundApp", ImmutableDictionary.Empty); } + /// + /// If the application is already running then it will be brought to the foreground. + /// + /// Represents the main gateway to interact with an app. + public static void ForegroundApp(this IApp app) + { + app.CommandExecutor.Execute("foregroundApp", ImmutableDictionary.Empty); + } + /// /// Reset the currently running app for this session. /// @@ -541,6 +550,41 @@ public static void Back(this IApp app) app.CommandExecutor.Execute("back", ImmutableDictionary.Empty); } + /// + /// Return the AppId of the running app. This is used inside any appium command that want the app id + /// + /// Represents the main gateway to interact with an app. + public static string GetAppId(this IApp app) + { + if (app is not AppiumApp aaa) + { + throw new InvalidOperationException($"GetAppId is only supported on AppiumApp"); + } + + var appId = aaa.Config.GetProperty("AppId"); + if (appId is not null) + { + return appId; + } + + throw new InvalidOperationException("AppId not found"); + } + + /// + /// Retrieve the target device this test is running against + /// + /// Represents the main gateway to interact with an app. + /// + /// + public static TestDevice GetTestDevice(this IApp app) + { + if (app is not AppiumApp aaa) + { + throw new InvalidOperationException($"GetTestDevice is only supported on AppiumApp"); + } + + return aaa.Config.GetProperty("TestDevice"); + } /// /// Check if element has focused @@ -609,4 +653,4 @@ static void WaitForNone(Func query, Wait(query, i => i == null, timeoutMessage, timeout, retryFrequency); } } -} \ No newline at end of file +} diff --git a/src/TestUtils/src/UITest.NUnit/UITestContextBase.cs b/src/TestUtils/src/UITest.NUnit/UITestContextBase.cs index f1ec79fc78fe..1f871cf041b5 100644 --- a/src/TestUtils/src/UITest.NUnit/UITestContextBase.cs +++ b/src/TestUtils/src/UITest.NUnit/UITestContextBase.cs @@ -43,7 +43,7 @@ public void InitialSetup(IServerContext context) InitialSetup(context, false); } - public void Reset() + public virtual void Reset() { if (_context == null) { From 813c7c93973e0032533aa71fd8485883e2738990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Thu, 22 Feb 2024 00:04:55 +0100 Subject: [PATCH 13/28] Implement gestures on Windows Spans (#17731) --- src/Controls/src/Core/Label/Label.Windows.cs | 42 +++++- .../Extensions/FormattedStringExtensions.cs | 120 ++++++++++++++---- .../net-windows/PublicAPI.Unshipped.txt | 1 + .../tests/UITests/Tests/Issues/Issue3525.cs | 5 - .../tests/UITests/Tests/LabelUITests.cs | 5 - 5 files changed, 136 insertions(+), 37 deletions(-) diff --git a/src/Controls/src/Core/Label/Label.Windows.cs b/src/Controls/src/Core/Label/Label.Windows.cs index ed89c6aa56b5..efbcbccd762d 100644 --- a/src/Controls/src/Core/Label/Label.Windows.cs +++ b/src/Controls/src/Core/Label/Label.Windows.cs @@ -1,12 +1,18 @@ #nullable disable +using System.Collections.Generic; using Microsoft.Maui.Controls.Platform; +using Microsoft.Maui.Graphics; +using Microsoft.UI.Xaml.Controls; namespace Microsoft.Maui.Controls { public partial class Label { - public static void MapDetectReadingOrderFromContent(LabelHandler handler, Label label) => MapDetectReadingOrderFromContent((ILabelHandler)handler, label); - public static void MapText(LabelHandler handler, Label label) => MapText((ILabelHandler)handler, label); + public static void MapDetectReadingOrderFromContent(LabelHandler handler, Label label) => + MapDetectReadingOrderFromContent((ILabelHandler)handler, label); + + public static void MapText(LabelHandler handler, Label label) => + MapText((ILabelHandler)handler, label); public static void MapDetectReadingOrderFromContent(ILabelHandler handler, Label label) => Platform.TextBlockExtensions.UpdateDetectReadingOrderFromContent(handler.PlatformView, label); @@ -19,5 +25,37 @@ public static void MapLineBreakMode(ILabelHandler handler, Label label) => public static void MapMaxLines(ILabelHandler handler, Label label) => handler.PlatformView?.UpdateMaxLines(label); + + protected override Size ArrangeOverride(Rect bounds) + { + var size = base.ArrangeOverride(bounds); + + RecalculateSpanPositions(); + + return size; + } + + void RecalculateSpanPositions() + { + if (Handler is not ILabelHandler labelHandler) + { + return; + } + + var platformView = labelHandler.PlatformView; + var virtualView = labelHandler.VirtualView as Label; + if (platformView is null || virtualView is null) + { + return; + } + + var formatted = virtualView.FormattedText; + if (formatted is null) + { + return; + } + + platformView.RecalculateSpanPositions(formatted); + } } } \ No newline at end of file diff --git a/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs index 5b29a13f1906..2eb096d9b251 100644 --- a/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs +++ b/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Graphics; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Documents; @@ -9,6 +11,15 @@ namespace Microsoft.Maui.Controls.Platform { public static class FormattedStringExtensions { + static double GetMeasuredLineHeight(DependencyObject obj) => + (double)obj.GetValue(MeasuredLineHeightProperty); + + static void SetMeasuredLineHeight(DependencyObject obj, double value) => + obj.SetValue(MeasuredLineHeightProperty, value); + + static readonly DependencyProperty MeasuredLineHeightProperty = DependencyProperty.RegisterAttached( + "MeasuredLineHeight", typeof(double), typeof(FormattedStringExtensions), new PropertyMetadata(0)); + public static void UpdateInlines(this TextBlock textBlock, Label label) => UpdateInlines( textBlock, @@ -31,44 +42,47 @@ public static void UpdateInlines( 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 textBlock.Measure(new global::Windows.Foundation.Size(double.MaxValue, double.MaxValue)); - var runAndColorTuples = formattedString.ToRunAndColorsTuples(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform); + var runs = formattedString.ToRunAndColorsTuples( + fontManager, + defaultLineHeight, + defaultHorizontalAlignment, + defaultFont, + defaultColor, + defaultTextTransform).ToArray(); - var heights = new List(); - int currentTextIndex = 0; - foreach (var runAndColorTuple in runAndColorTuples) + var lineHeights = new List(runs.Length); + foreach (var (run, _, _) in runs) { - Run run = runAndColorTuple.Item1; - Color textColor = runAndColorTuple.Item2; - Color background = runAndColorTuple.Item3; - heights.Add(textBlock.FindDefaultLineHeight(run)); - textBlock.Inlines.Add(run); - int length = run.Text.Length; + lineHeights.Add(textBlock.FindDefaultLineHeight(run)); + } - if (background != null || textColor != null) - { - TextHighlighter textHighlighter = new TextHighlighter { Ranges = { new TextRange(currentTextIndex, length) } }; + var currentTextIndex = 0; + for (var i = 0; i < runs.Length; i++) + { + var (run, textColor, background) = runs[i]; + var runTextLength = run.Text.Length; - if (background != null) - { - textHighlighter.Background = background.ToPlatform(); - } - else - { - textHighlighter.Background = Colors.Transparent.ToPlatform(); - } + SetMeasuredLineHeight(run, lineHeights[i]); - if (textColor != null) + textBlock.Inlines.Add(run); + + if (background is not null || textColor is not null) + { + var textHighlighter = new TextHighlighter { - textHighlighter.Foreground = textColor.ToPlatform(); - } + Ranges = { new TextRange(currentTextIndex, runTextLength) }, + Background = (background ?? Colors.Transparent).ToPlatform(), + Foreground = textColor?.ToPlatform(), + }; textBlock.TextHighlighters.Add(textHighlighter); } - currentTextIndex += length; + currentTextIndex += runTextLength; } } @@ -127,5 +141,61 @@ public static Tuple ToRunAndColorsTuple( return Tuple.Create(run, span.TextColor, span.BackgroundColor); } + + internal static void RecalculateSpanPositions(this TextBlock control, FormattedString formatted) + { + var spans = formatted.Spans; + var labelWidth = control.ActualWidth; + if (spans is null || spans.Count == 0 || labelWidth <= 0 || control.Height <= 0) + { + return; + } + + for (int i = 0; i < spans.Count; i++) + { + var span = spans[i]; + + var inline = control.Inlines.ElementAt(i); + + var startRect = inline.ContentStart.GetCharacterRect(LogicalDirection.Forward); + var endRect = inline.ContentEnd.GetCharacterRect(LogicalDirection.Forward); + + var defaultLineHeight = GetMeasuredLineHeight(inline); + var yaxis = startRect.Top; + + var lineHeights = new List(); + + while (yaxis < endRect.Bottom) + { + double lineHeight; + if (yaxis == startRect.Top) + { + // First Line + lineHeight = startRect.Bottom - startRect.Top; + } + else if (yaxis != endRect.Top) + { + // Middle Line(s) + lineHeight = defaultLineHeight; + } + else + { + // Bottom Line + lineHeight = endRect.Bottom - endRect.Top; + } + + lineHeights.Add(lineHeight); + yaxis += lineHeight; + } + + var region = Region.FromLines( + lineHeights.ToArray(), + labelWidth, + startRect.X, + endRect.X + endRect.Width, + startRect.Top); + ((ISpatialElement)span).Region = region.Inflate(10); + } + } } } \ No newline at end of file diff --git a/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt index fa1ca57b7941..b066a2e652e0 100644 --- a/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt @@ -52,6 +52,7 @@ Microsoft.Maui.Controls.InputView.FontSize.get -> double Microsoft.Maui.Controls.InputView.FontSize.set -> void Microsoft.Maui.Controls.InputView.SelectionLength.get -> int Microsoft.Maui.Controls.InputView.SelectionLength.set -> void +override Microsoft.Maui.Controls.Label.ArrangeOverride(Microsoft.Maui.Graphics.Rect bounds) -> Microsoft.Maui.Graphics.Size static Microsoft.Maui.Controls.Handlers.ShellItemHandler.MapTitle(Microsoft.Maui.Controls.Handlers.ShellItemHandler! handler, Microsoft.Maui.Controls.ShellItem! item) -> void static Microsoft.Maui.Controls.Handlers.ShellSectionHandler.MapTitle(Microsoft.Maui.Controls.Handlers.ShellSectionHandler! handler, Microsoft.Maui.Controls.ShellSection! item) -> void Microsoft.Maui.Controls.Handlers.Compatibility.ViewRenderer.ViewRenderer(Microsoft.Maui.IPropertyMapper! mapper, Microsoft.Maui.CommandMapper? commandMapper = null) -> void diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue3525.cs b/src/Controls/tests/UITests/Tests/Issues/Issue3525.cs index 0430a77dfdbd..0a04adb6fa83 100644 --- a/src/Controls/tests/UITests/Tests/Issues/Issue3525.cs +++ b/src/Controls/tests/UITests/Tests/Issues/Issue3525.cs @@ -24,11 +24,6 @@ public void SpanRegionClicking() Assert.Ignore("Click (x, y) pointer type mouse is not implemented."); } - if (Device == TestDevice.Windows) - { - Assert.Ignore("This test is failing on Windows because the feature is not yet implemented: https://github.com/dotnet/maui/pull/17731"); - } - var label = App.WaitForElement(kLabelTestAutomationId); var location = label.GetRect(); diff --git a/src/Controls/tests/UITests/Tests/LabelUITests.cs b/src/Controls/tests/UITests/Tests/LabelUITests.cs index 5c401c041d7a..3c00cb850b3d 100644 --- a/src/Controls/tests/UITests/Tests/LabelUITests.cs +++ b/src/Controls/tests/UITests/Tests/LabelUITests.cs @@ -31,11 +31,6 @@ public void SpanTapped() Assert.Ignore("Click (x, y) pointer type mouse is not implemented."); } - if (Device == TestDevice.Windows) - { - Assert.Ignore("This test is failing on Windows because the feature is not yet implemented: https://github.com/dotnet/maui/issues/4734"); - } - var remote = new EventViewContainerRemote(UITestContext, Test.FormattedString.SpanTapped); remote.GoTo(); From 85adc13a249fe4ac59da6518ea5dc70055ee569c Mon Sep 17 00:00:00 2001 From: Tim Miller Date: Thu, 22 Feb 2024 12:08:00 +0900 Subject: [PATCH 14/28] [iOS/Catalyst] Correctly call DidFinishNavigation in NavigationDelegate (#20725) * Invoke ProcessNavigatedAsync for MauiWKWebView * Add underlying API back * Revert sandbox changes * Use FireAndForget --- src/Core/src/Platform/iOS/MauiWKWebView.cs | 2 +- src/Core/src/Platform/iOS/MauiWebViewNavigationDelegate.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Core/src/Platform/iOS/MauiWKWebView.cs b/src/Core/src/Platform/iOS/MauiWKWebView.cs index 196986708ef9..82d00c5130d9 100644 --- a/src/Core/src/Platform/iOS/MauiWKWebView.cs +++ b/src/Core/src/Platform/iOS/MauiWKWebView.cs @@ -76,7 +76,7 @@ public override void MovedToWindow() _movedToWindow?.Invoke(this, EventArgs.Empty); } - [Export("webView:didFinishNavigation:")] + [Obsolete("Use MauiWebViewNavigationDelegate.DidFinishNavigation instead.")] public async void DidFinishNavigation(WKWebView webView, WKNavigation navigation) { var url = CurrentUrl; diff --git a/src/Core/src/Platform/iOS/MauiWebViewNavigationDelegate.cs b/src/Core/src/Platform/iOS/MauiWebViewNavigationDelegate.cs index 9a166b82c2e2..6f1a916c218b 100644 --- a/src/Core/src/Platform/iOS/MauiWebViewNavigationDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiWebViewNavigationDelegate.cs @@ -43,7 +43,11 @@ public void DidFinishNavigation(WKWebView webView, WKNavigation navigation) virtualView.Navigated(_lastEvent, url, WebNavigationResult.Success); - handler.PlatformView?.UpdateCanGoBackForward(virtualView); + // ProcessNavigatedAsync calls UpdateCanGoBackForward + if (handler is WebViewHandler webViewHandler) + webViewHandler.ProcessNavigatedAsync(url).FireAndForget(); + else + handler.PlatformView?.UpdateCanGoBackForward(virtualView); } [Export("webView:didFailNavigation:withError:")] From 373b060508ce471fcf03e3b46597958b865984d9 Mon Sep 17 00:00:00 2001 From: Mike Corsaro Date: Thu, 22 Feb 2024 06:48:40 -0800 Subject: [PATCH 15/28] [Window] Fix transforms being offset by 0.5x0.5 (#20454) * Fix transforms being offset by 0.5x0.5 * Added visual regression test * Adjust tests * Fix ref image * update baseline img again... --------- Co-authored-by: Mike Corsaro --- .../Controls.Sample.UITests.csproj | 2 + .../Issues/Issue17694.cs | 57 ++++++++++++++++++ .../tests/UITests/Tests/Issues/Issue17694.cs | 33 ++++++++++ .../snapshots/windows/Issue17694Test.png | Bin 0 -> 6226 bytes .../Windows/TransformationExtensions.cs | 2 - 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/Controls/samples/Controls.Sample.UITests/Issues/Issue17694.cs create mode 100644 src/Controls/tests/UITests/Tests/Issues/Issue17694.cs create mode 100644 src/Controls/tests/UITests/snapshots/windows/Issue17694Test.png diff --git a/src/Controls/samples/Controls.Sample.UITests/Controls.Sample.UITests.csproj b/src/Controls/samples/Controls.Sample.UITests/Controls.Sample.UITests.csproj index 7c4aa8789822..7ddb8c94e20c 100644 --- a/src/Controls/samples/Controls.Sample.UITests/Controls.Sample.UITests.csproj +++ b/src/Controls/samples/Controls.Sample.UITests/Controls.Sample.UITests.csproj @@ -31,11 +31,13 @@ + + diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issue17694.cs b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue17694.cs new file mode 100644 index 000000000000..ba10bcef58a5 --- /dev/null +++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue17694.cs @@ -0,0 +1,57 @@ +using Microsoft.Maui.Controls; +using Microsoft.Maui.Graphics; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 17694, "Circle view not rotating from center", PlatformAffected.UWP)] + public class Issue17694 : TestContentPage + { + protected override void Init() + { + var circleView = new CircleView + { + HeightRequest = 50, + WidthRequest = 50, + Drawable = new CircleDraw() + }; + + var button = new Button() + { + AutomationId = "Spin", + Text = "Spin", + }; + button.Clicked += (s, e) => + { + circleView.Rotation = 180; + }; + + var stack = new VerticalStackLayout + { + circleView, + button + }; + + Content = stack; + } + + public class CircleView : GraphicsView + { + public CircleView() + { + + } + } + + public class CircleDraw : IDrawable + { + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.FillColor = Colors.Red; + canvas.FillCircle(25, 25, 25); + canvas.StrokeSize = 1f; + canvas.StrokeColor = Colors.Black; + canvas.DrawLine(0, 25, 50, 25); + } + } + } +} diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue17694.cs b/src/Controls/tests/UITests/Tests/Issues/Issue17694.cs new file mode 100644 index 000000000000..c24ffd2382b5 --- /dev/null +++ b/src/Controls/tests/UITests/Tests/Issues/Issue17694.cs @@ -0,0 +1,33 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.AppiumTests.Issues +{ + public class Issue17694 : _IssuesUITest + { + public Issue17694(TestDevice device) : base(device) + { + } + + public override string Issue => "Circle view not rotating from center"; + + [Test] + public void Issue17694Test() + { + this.IgnoreIfPlatforms(new[] + { + TestDevice.iOS, + TestDevice.Mac, + TestDevice.Android + }); + + App.WaitForElement("Spin"); + + // 1. Click button + App.Click("Spin"); + + VerifyScreenshot(); + } + } +} diff --git a/src/Controls/tests/UITests/snapshots/windows/Issue17694Test.png b/src/Controls/tests/UITests/snapshots/windows/Issue17694Test.png new file mode 100644 index 0000000000000000000000000000000000000000..2527dca6fed39e78d7148ade4fbb38092f5ff945 GIT binary patch literal 6226 zcmdT|d0Z1l7M=(dTC1UT(Y6S2G&f z%>L+$j{pF(H*fmJ3IL@(0A|#y&VVCv3(hZx{}KGGEWQSr>;)b0i?Z9g?dt%@j+!;H z?*sV#L*GrdegMoVLH`Kqf87!W!2Gt&->lmm;M65>j&Ks+sOarCjrogkruv0X_~ zC}}t=F7HfoT(mOp=3mQCEZ=>(fqSCBcQfTe(j9{hTYsd{4({74{#y0fyrOSKKYpva zC1_{F{4v5(<8LY3b{p)y^4V(kp$G+@b90_JN> zn*lIiYfA*6`3w1{4VWJnb_i#UXB(3eUIST z5%}!*bQKWhxqUIRBLV##fF_jGzxWh@nK$EO8=jws1|>-z)q6uPzUKI7nLR@zIv!|+ zeDwxs`8|Nm;-1SHyY0qU48X!mX%k*eS}6A&+g7yF#KgqZl&?vM_Lhwavnx`Q6EdBS z-)<dw<2N!}E7m_*Cu-&IHLn7#5lv<{CNjk zV`79xtd7=}zDnoFms(+3FhNKC`CgT*+SflQRp#bshb@6aB*RsHITY5g@u0xS?j_OO znX@adx@#klwANY_?yIk=+CwBgtpWp7|7B>_FKiOaH~7xNo3NP@_#UTLSEE zr@A$Ex&q1VaQiZTvDVEjp>RO|D4df+@i7{(AX#;5X-ydva=#Tt8330rFI~Enn@h`7X;0{9 zr@6W^3Ut{ddf?bF#K>r)jR=~~v}G|hB)7tomG8By=bneS>DdzRU3?T6#jsLR9 zmRMsm-=SvqXl?1P8GA3lS!sG%={h=$0^$yn14p-j0V`7e@N;kFn>O6^l;zC3anctT zw{A>UHrMCmUMYM$V}QfW5Y(t#bL7rOQ1)NnE+R-$$s*tMdxUm+VE>PqT%`eyM9j(O z`S{&aT^!amF+=hanYxsc8GAp3DSFv3zo(ck z;bg7NY8@*|&&F#hD1*RNl9c6w5j%^6B6so`3w z@u7^smMiW(jkQ_+2lK_O(9w1qQwQJscRDN|3N=CCUV#lux{h02U9D|r5UK${jeVL; zmQ%rzmNV*l4aWsMChx%L{gW#5=gqUuuv;Cf1ZTK;6|g~uEd{2LB=aih3sIXx;q zP?!=68F^9Ox~7@IalKxTqvr6*M9npM?wr&zI^^W9th1a*P8*sI6pIF68%fR-5fOz) zKzPOp8dLhq_c%X-exUNIzNR7MUs9k!U0q$5^r#B>I3iu;A=hxU%BX3Cbyh3*&Hd!~ zz|^49T2DSLBZS@!!U(Wc-FKbuc(_dcRq6IP!(y`18yVyfRocGNpWbFgne{y!dJ_Wo zqc;YM0v>G_F+`afWTsnn{4#smxippT@9SE zV@1YoKhAeLs7DUeD==){;o1(AQhHKI?70 z?#J#(Dmx$%D4Q>Y4P^n(!LN!LB4wOhTr|>GsbS>ZIR|_Ot69ynI-b{-(GxbC8UcG(TjjhO|lv4gxiXNuq$r72`^ zs8`d}|Jl%|d!M01%B@~o=vo!qO#pq{EiHTa^qxr5?)FH+XPHVWbAnr*$d2U0=*gqy z_Q@P5dF&ld^*6z)2j;Z>XyAdoho8?0<)rSJHEY(Z?_sws8yOi9a?<$wE{pYSGJ{1K z^m*DU0PHUseaoLacW#xjh~II8(s*3Vv4P3+)S&AmY~s}X+QuZ<6pOj8!5aNti*LT>cT_rC8)=Q`g94uS&vTJISkFxA zjpi+rdS=?r|2u<;*uVGlbzGMy4BnW%{C&xyj%3jAgiadQQyLC4#`P3dND8*zeYqs} z*Ahx$f?SJXV>`~kHa#x4)|~Z)c+0ht_-U(O;U`eX?UspO{NxQg$x4oX@d`dtNQqXK zj_vLcDVH=#Pon@0Eu!C=0m6cB$H&GBUEwBH*{U!`FKtgj0#GoIy#pPVg#?vcq&cJr ztJ>llHKE{*5xSS!Fz}H*b1sv`KC;s{u$Kb0 zUzkviMQ7yMl2@;q*I0((12q&e5 z6TEg52~YFMY;w`_FCmQEDG$FqczDbDsT*fLby_(K%4{7Sca%Bx*ed(%l0^#uP_=m; z2`?p2mi-L{L6v79_lB+<(tJ1w^NXSzgi`k@ZFR^LHX9C*hI;P4(Y`(Q%ioZa9wdIbuK7 z;c^LPKg=Y}7=mCNqJqlzV!_1F!}Txyxww0EiY&=pVo0r8vyG0Fo(OEN%i zEA%NRQbQ3O+0=@dVZ4NPPa&@VKI{rmE1da93Q6JmuTUU7*N~2luruvEj8w=U=c^of zDdNu(b@XJR@R~5yGT&)wS}q%+Gp7>|E_#@&JI=rh5_}{<0hco{zvKjZ+*o)WMI!*W zsetqOFz#xZ0f+$-_4YVMHB8{|qPKl=d)j_oNc9S==YpSS%YZ5QHAc&t-LdHl5jdyPa=zP0TUmce7;BDMM>H9YaXiKN0JJQ+k+487ytu0+xr`h^e|our&6v{;N`Q-}P0_v9GABdsr0~Om&OidZr0!nCqXM zy}-?{WNq8dg!VU+G^#`C_O literal 0 HcmV?d00001 diff --git a/src/Core/src/Platform/Windows/TransformationExtensions.cs b/src/Core/src/Platform/Windows/TransformationExtensions.cs index 489b034d6e6d..c9c5f9cd3cd2 100644 --- a/src/Core/src/Platform/Windows/TransformationExtensions.cs +++ b/src/Core/src/Platform/Windows/TransformationExtensions.cs @@ -51,8 +51,6 @@ public static void UpdateTransformation(this FrameworkElement frameworkElement, { frameworkElement.RenderTransform = new CompositeTransform { - CenterX = anchorX, - CenterY = anchorY, Rotation = rotation, ScaleX = scaleX, ScaleY = scaleY, From ef43ee17863fd6a5469b7b2f820594e10b96d030 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Fri, 23 Feb 2024 14:27:52 +0000 Subject: [PATCH 16/28] =?UTF-8?q?[ci]=C2=A0Update=20azdo=20BuildNumber=20(?= =?UTF-8?q?#20751)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Versions.targets * Fix variable --- eng/Versions.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.targets b/eng/Versions.targets index 1b8398d1090a..2419b505db60 100644 --- a/eng/Versions.targets +++ b/eng/Versions.targets @@ -99,7 +99,7 @@ - + From 3423bad25799c8002e5247045b52dbdab6f5394c Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Fri, 23 Feb 2024 15:49:20 +0000 Subject: [PATCH 17/28] [ci] Add mono back to provisioning (#20813) --- eng/provisioning/provisioning.csx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eng/provisioning/provisioning.csx b/eng/provisioning/provisioning.csx index f98455b9c639..00a3303539f5 100644 --- a/eng/provisioning/provisioning.csx +++ b/eng/provisioning/provisioning.csx @@ -2,6 +2,8 @@ if (IsMac) { ForceJavaCleanup(); MicrosoftOpenJdk ("11.0.13.8.1"); + //this is needed for tools on macos like nunit console or nuget.exe + Item("https://download.mono-project.com/archive/6.12.0/macos-10-universal/MonoFramework-MDK-6.12.0.199.macos10.xamarin.universal.pkg"); AppleCodesignIdentity("Apple Development: Jonathan Dick (FJL7285DY2)", "https://dl.internalx.com/qa/code-signing-entitlements/components-mac-ios-certificate.p12"); AppleCodesignProfile("https://dl.internalx.com/qa/code-signing-entitlements/components-ios-provisioning.mobileprovision"); AppleCodesignProfile("https://dl.internalx.com/qa/code-signing-entitlements/components-mac-provisioning.mobileprovision"); From 0de4f227938402f62c6eee2919ded6ff312ea9f9 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Fri, 23 Feb 2024 17:59:11 +0200 Subject: [PATCH 18/28] Add the trailing .0 to all the things (#20792) This is just to be consistent and avoid a potential issue I saw when testing the potential Win2D update. I am not sure why this is needed, but appears to sometimes be needed in net7.0 scenarios. --- Directory.Build.props | 4 ++-- eng/devices/windows.cake | 2 +- eng/pipelines/common/device-tests.yml | 2 +- eng/pipelines/common/ui-tests.yml | 2 +- .../src/WinUI/Compatibility.ControlGallery.WinUI.csproj | 2 +- .../Core/tests/WinUI/Compatibility.Windows.UnitTests.csproj | 2 +- src/Controls/tests/UITests/UITest.cs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index df9aba56e94c..5ce21d045967 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -89,8 +89,8 @@ 13.5 10.14 30.0 - 10.0.19041 - 10.0.20348 + 10.0.19041.0 + 10.0.20348.0 6.5 diff --git a/eng/devices/windows.cake b/eng/devices/windows.cake index d9cee85ea9ff..bee4cbda1602 100644 --- a/eng/devices/windows.cake +++ b/eng/devices/windows.cake @@ -5,7 +5,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -const string defaultVersion = "10.0.19041"; +const string defaultVersion = "10.0.19041.0"; const string dotnetVersion = "net8.0"; // required diff --git a/eng/pipelines/common/device-tests.yml b/eng/pipelines/common/device-tests.yml index 8e102da8c53c..1f44dd41e485 100644 --- a/eng/pipelines/common/device-tests.yml +++ b/eng/pipelines/common/device-tests.yml @@ -167,7 +167,7 @@ stages: parameters: platform: windows path: $(PROJECT_PATH) - apiVersion: 10.0.19041 + apiVersion: 10.0.19041.0 windowsPackageId: $(PACKAGE_ID) device: $(DEVICE) # For Windows this switches between packaged and unpackaged provisionatorChannel: ${{ parameters.provisionatorChannel }} diff --git a/eng/pipelines/common/ui-tests.yml b/eng/pipelines/common/ui-tests.yml index ad5aaf536c7e..639c8de45dfc 100644 --- a/eng/pipelines/common/ui-tests.yml +++ b/eng/pipelines/common/ui-tests.yml @@ -104,7 +104,7 @@ stages: - template: ui-tests-steps.yml parameters: platform: windows - version: "10.0.19041" + version: "10.0.19041.0" device: windows10 path: ${{ project.winui }} app: ${{ project.app }} diff --git a/src/Compatibility/ControlGallery/src/WinUI/Compatibility.ControlGallery.WinUI.csproj b/src/Compatibility/ControlGallery/src/WinUI/Compatibility.ControlGallery.WinUI.csproj index 124debe0e66e..75c82319dc84 100644 --- a/src/Compatibility/ControlGallery/src/WinUI/Compatibility.ControlGallery.WinUI.csproj +++ b/src/Compatibility/ControlGallery/src/WinUI/Compatibility.ControlGallery.WinUI.csproj @@ -4,7 +4,7 @@ WinExe - $(_MauiDotNetTfm)-windows10.0.19041 + $(_MauiDotNetTfm)-windows10.0.19041.0 10.0.17134.0 Microsoft.Maui.Controls.Compatibility.ControlGallery.WinUI app.manifest diff --git a/src/Compatibility/Core/tests/WinUI/Compatibility.Windows.UnitTests.csproj b/src/Compatibility/Core/tests/WinUI/Compatibility.Windows.UnitTests.csproj index 48868b338986..b01089bc8e22 100644 --- a/src/Compatibility/Core/tests/WinUI/Compatibility.Windows.UnitTests.csproj +++ b/src/Compatibility/Core/tests/WinUI/Compatibility.Windows.UnitTests.csproj @@ -1,7 +1,7 @@  - $(_MauiDotNetTfm)-windows10.0.19041 + $(_MauiDotNetTfm)-windows10.0.19041.0 10.0.17134.0 Microsoft.Maui.Controls.Compatibility.UAP.UnitTests Microsoft.Maui.Controls.Compatibility.UAP.UnitTests diff --git a/src/Controls/tests/UITests/UITest.cs b/src/Controls/tests/UITests/UITest.cs index 8df106671355..9768a70f2067 100644 --- a/src/Controls/tests/UITests/UITest.cs +++ b/src/Controls/tests/UITests/UITest.cs @@ -74,7 +74,7 @@ public override IConfig GetTestConfig() var appProjectFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "..\\..\\..\\..\\..\\samples\\Controls.Sample.UITests"); var appProjectPath = Path.Combine(appProjectFolder, "Controls.Sample.UITests.csproj"); var windowsExe = "Controls.Sample.UITests.exe"; - var windowsExePath = Path.Combine(appProjectFolder, $"bin\\{configuration}\\{frameworkVersion}-windows10.0.20348\\win10-x64\\{windowsExe}"); + var windowsExePath = Path.Combine(appProjectFolder, $"bin\\{configuration}\\{frameworkVersion}-windows10.0.20348.0\\win10-x64\\{windowsExe}"); var appPath = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WINDOWS_APP_PATH")) ? windowsExePath From 28fdc7e14a4b48561805b10a47f651d413a79f51 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Fri, 23 Feb 2024 17:59:37 +0200 Subject: [PATCH 19/28] Support the unpackaged format for font loading (#20790) --- .../FontImageSourceService.Windows.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs b/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs index 56567b43cbf7..91dbff0e08cc 100644 --- a/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs +++ b/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs @@ -6,8 +6,10 @@ using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Text; using Microsoft.Graphics.Canvas.UI.Xaml; +using Microsoft.Maui.ApplicationModel; using Microsoft.Maui.Devices; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Storage; using WImageSource = Microsoft.UI.Xaml.Media.ImageSource; namespace Microsoft.Maui @@ -122,6 +124,18 @@ string GetFontSource(IFontImageSource imageSource) } } + // unpackaged apps can't load files using packaged schemes + if (!AppInfoUtils.IsPackagedApp) + { + var fontUri = new Uri(fontSource, UriKind.RelativeOrAbsolute); + + var path = fontUri.AbsolutePath.TrimStart('/'); + if (FileSystemUtils.TryGetAppPackageFileUri(path, out var uri)) + { + fontSource = uri + fontUri.Fragment; + } + } + return fontSource; } } From 79faaf53426522122127f79aed2f916123d7e931 Mon Sep 17 00:00:00 2001 From: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:01:54 +0100 Subject: [PATCH 20/28] [iOS] NullReferenceException for Span GridItemsLayout fix (#20492) * [iOS] NRE in GridViewLayout & xaml test (#18828) * Added a UiTest (#19803) --- .../Issues/Issue19803.xaml | 33 ++++++++++++ .../Issues/Issue19803.xaml.cs | 28 ++++++++++ .../Core/Handlers/Items/iOS/GridViewLayout.cs | 2 +- .../tests/UITests/Tests/Issues/Issue19803.cs | 27 ++++++++++ .../tests/Xaml.UnitTests/Issues/Bz18828.xaml | 16 ++++++ .../Xaml.UnitTests/Issues/Bz18828.xaml.cs | 52 +++++++++++++++++++ 6 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/Controls/samples/Controls.Sample.UITests/Issues/Issue19803.xaml create mode 100644 src/Controls/samples/Controls.Sample.UITests/Issues/Issue19803.xaml.cs create mode 100644 src/Controls/tests/UITests/Tests/Issues/Issue19803.cs create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Bz18828.xaml create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Bz18828.xaml.cs diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issue19803.xaml b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue19803.xaml new file mode 100644 index 000000000000..d88254ec838c --- /dev/null +++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue19803.xaml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + Item 8 + Item 9 + + + +