Merge branch 'main' into merge-main-net9

This commit is contained in:
Rui Marinho 2024-08-27 16:41:19 +01:00
Родитель e2427dfad6 bae06d325a
Коммит d9f2257965
30 изменённых файлов: 517 добавлений и 56 удалений

2
.github/ISSUE_TEMPLATE/bug-report.yml поставляемый
Просмотреть файл

@ -7,8 +7,6 @@ body:
attributes:
value: |
Thanks for taking the time to fill out this bug report! Please make sure to add as much detail as you can, preferably with a [reproduction](https://github.com/dotnet/maui/blob/main/.github/repro.md). This will help us diagnose the issue faster and thus resolve it quicker.
If this is a bug in Xamarin.Forms, please [open the issue in the Xamarin.Forms repo](https://github.com/xamarin/Xamarin.Forms/issues/new?assignees=&labels=s%2Funverified%2C+t%2Fbug+%3Abug%3A&template=bug_report.md&title=[Bug]+).
- type: textarea
id: description
attributes:

Просмотреть файл

@ -52,12 +52,6 @@ stages:
downloadPath: $(Build.StagingDirectory)\nuget-signed
displayName: Download nuget-signed
- task: DownloadPipelineArtifact@2
inputs:
artifactName: vs-msi-nugets
downloadPath: $(Build.StagingDirectory)\nuget-signed
displayName: Download vs-msi-nugets
- powershell: |
$feed = "$env:NUGET_FEED_MAUI_NIGHTLY"
# Signed maui packages except manifest

Просмотреть файл

@ -22,6 +22,25 @@ namespace Microsoft.Maui.Controls.Platform
}
else
{
// If the group items are not an IList, then we'll have to append the footer the hard way
var groupFooterTemplateItemType = footerItemTemplateContext.Item?.GetType();
if (groupFooterTemplateItemType is not IList)
{
var listPlusFooter = new List<object>();
foreach (var item in (items as IEnumerable))
{
listPlusFooter.Add(item);
}
listPlusFooter.Add(footerItemTemplateContext);
Items = listPlusFooter;
return;
}
// UWP ListViewBase does not support group footers. So we're going to fake the footer by adding an
// extra item to the ItemsSource so the footer shows up at the end of the group.
@ -32,19 +51,6 @@ namespace Microsoft.Maui.Controls.Platform
Items = itemsList;
return;
}
// If the group items are not an IList, then we'll have to append the footer the hard way
var listPlusFooter = new List<object>();
foreach (var item in (items as IEnumerable))
{
listPlusFooter.Add(item);
}
listPlusFooter.Add(footerItemTemplateContext);
Items = listPlusFooter;
}
}
}

Просмотреть файл

@ -92,34 +92,7 @@ namespace Microsoft.Maui.DeviceTests
Assert.Contains(shellSectionGrid, (shellSection as IVisualTreeElement).GetVisualChildren());
Assert.Contains(shellContentGrid, (shellContent as IVisualTreeElement).GetVisualChildren());
});
}
[Fact]
public async Task FlyoutHeaderAdaptsToMinimumHeight()
{
await RunShellTest(shell =>
{
var layout = new VerticalStackLayout()
{
new Label() { Text = "Flyout Header" }
};
layout.MinimumHeightRequest = 30;
shell.FlyoutHeader = layout;
shell.FlyoutHeaderBehavior = FlyoutHeaderBehavior.CollapseOnScroll;
},
async (shell, handler) =>
{
await OpenFlyout(handler);
var flyoutFrame = GetFrameRelativeToFlyout(handler, shell.FlyoutHeader as IView);
await AssertionExtensions.AssertEventually(() =>
{
return Math.Abs(30 - flyoutFrame.Height) < 0.2;
}, message: $"Expected: {30}. Actual: {flyoutFrame.Height}. Diff: {Math.Abs(30 - flyoutFrame.Height)}");
});
}
}
#if !WINDOWS
[Theory]

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 85 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 86 KiB

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue18443">
<Entry AutomationId="entry" x:Name="MauiEntry" Text="Microsoft Maui Entry" SelectionLength="5" HeightRequest="75"></Entry>
</ContentPage>

Просмотреть файл

@ -0,0 +1,21 @@
namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 18443, "SelectionLength Property Not Applied to Entry at Runtime", PlatformAffected.Android)]
public partial class Issue18443 : ContentPage
{
public Issue18443()
{
InitializeComponent();
}
protected override async void OnAppearing()
{
base.OnAppearing();
await Task.Delay(1000);
MauiEntry.Focus();
}
}
}

Просмотреть файл

@ -0,0 +1,202 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using Microsoft.Maui.Controls.Internals;
namespace Maui.Controls.Sample.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 18702, "When the CollectionView has a group footer template it should not crash the application")]
public class Issue18702 : TestContentPage
{
protected override void Init()
{
var layout = new StackLayout();
var bindingContext = new _18702viewModel();
var collectionView = new CollectionView();
collectionView.BindingContext = bindingContext;
collectionView.IsGrouped = true;
collectionView.AutomationId = "collectionView";
collectionView.ItemTemplate = new DataTemplate(() =>
{
var border = new Border
{
BackgroundColor = Colors.Red,
HeightRequest = 20
};
var label = new Label();
label.SetBinding(Label.TextProperty, ".");
border.Content = label;
return border;
});
collectionView.GroupHeaderTemplate = new DataTemplate(() =>
{
var border = new Border
{
BackgroundColor = Colors.Yellow,
HeightRequest = 20
};
var label = new Label();
label.SetBinding(Label.TextProperty, "Key");
border.Content = label;
return border;
});
collectionView.GroupFooterTemplate = new DataTemplate(() =>
{
var border = new Border
{
BackgroundColor = Colors.Green,
HeightRequest = 20
};
var label = new Label();
label.SetBinding(Label.TextProperty, "Total");
border.Content = label;
return border;
});
// Set the ItemsSource binding
collectionView.SetBinding(CollectionView.ItemsSourceProperty, "Data");
layout.Children.Add(collectionView);
Content = layout;
}
public class _18702viewModel
{
public ObservableCollection<_18702Group> Data { get; set; } = [new("group A", [1, 2, 3])];
}
[Preserve(AllMembers = true)]
public class _18702Group : Grouping<string, int>
{
public _18702Group(string key, IEnumerable<int> items) : base(key, items)
{
}
public int Total => Items.Sum();
}
[Preserve(AllMembers = true)]
public class Grouping<TKey, TItem> : ObservableRangeCollection<TItem>, INotifyPropertyChanged
{
public TKey Key { get; }
public new ICollection<TItem> Items => base.Items;
public Grouping(TKey key, IEnumerable<TItem> items)
{
Key = key;
AddRange(items);
}
}
/// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
/// </summary>
/// <typeparam name="T"></typeparam>
[Preserve(AllMembers = true)]
public class ObservableRangeCollection<T> : ObservableCollection<T>
{
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
/// </summary>
public ObservableRangeCollection()
: base()
{
}
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
/// </summary>
/// <param name="collection">collection: The collection from which the elements are copied.</param>
/// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception>
public ObservableRangeCollection(IEnumerable<T> collection)
: base(collection)
{
}
/// <summary>
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
/// </summary>
public void AddRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Add)
{
if (collection == null)
throw new ArgumentNullException("collection");
CheckReentrancy();
if (notificationMode == NotifyCollectionChangedAction.Reset)
{
foreach (var i in collection)
{
Items.Add(i);
}
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
return;
}
int startIndex = Count;
var changedItems = collection is List<T> ? (List<T>)collection : new List<T>(collection);
foreach (var i in changedItems)
{
Items.Add(i);
}
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, changedItems, startIndex));
}
/// <summary>
/// Removes the first occurrence of each item in the specified collection from ObservableCollection(Of T).
/// </summary>
public void RemoveRange(IEnumerable<T> collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
foreach (var i in collection)
Items.Remove(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// Clears the current collection and replaces it with the specified item.
/// </summary>
public void Replace(T item)
{
ReplaceRange(new T[] { item });
}
/// <summary>
/// Clears the current collection and replaces it with the specified collection.
/// </summary>
public void ReplaceRange(IEnumerable<T> collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
Items.Clear();
AddRange(collection, NotifyCollectionChangedAction.Reset);
}
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue19152"
Title="Issue19152">
<ScrollView>
<VerticalStackLayout
Padding="30,0"
Spacing="25">
<Entry Background="White" AutomationId="entry" x:Name="entry" TextColor="Blue" Text="Entry Text" ClearButtonVisibility="WhileEditing"/>
<Button AutomationId="button"
x:Name="button"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Fill" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>

Просмотреть файл

@ -0,0 +1,16 @@
namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 19152, "Windows | Entry ClearButton not taking color of text", PlatformAffected.UWP)]
public partial class Issue19152 : ContentPage
{
public Issue19152()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
entry.Focus();
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue19500">
<Editor
HeightRequest="100"
IsReadOnly="True"
AutomationId="editor"
Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla porttitor mauris non ornare ultrices. Ut semper ultrices justo eget semper.
Ut imperdiet dolor ut vestibulum molestie. Duis a libero ex. Etiam mi urna, lobortis sed tincidunt in, tempus eget magna. Aenean quis malesuada eros.
Phasellus felis eros, condimentum et tortor sed, condimentum convallis turpis. Sed in varius metus, at auctor orci. Maecenas luctus nibh nibh,
nec aliquam est fermentum in. Etiam consectetur lectus erat, sed placerat sapien rutrum eu. Suspendisse tincidunt fermentum tempor.
Maecenas egestas neque nec lacinia fringilla."/>
</ContentPage>

Просмотреть файл

@ -0,0 +1,15 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;
namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 19500, "[iOS] Editor is not be able to scroll if IsReadOnly is true", PlatformAffected.iOS)]
public partial class Issue19500 : ContentPage
{
public Issue19500()
{
InitializeComponent();
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue24284"
xmlns:ns="clr-namespace:Maui.Controls.Sample.Issues">
<ShellContent Title="Page 1">
<ContentPage>
<Label HeightRequest="30" AutomationId="HeightReferenceLabel" Text="Hello, World!"/>
</ContentPage>
</ShellContent>
<ShellContent Title="Page 2">
<ContentPage/>
</ShellContent>
</Shell>

Просмотреть файл

@ -0,0 +1,22 @@
namespace Maui.Controls.Sample.Issues;
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 24284, "FlyoutHeaderAdaptsToMinimumHeight", PlatformAffected.All)]
public partial class Issue24284 : Shell
{
public Issue24284()
{
InitializeComponent();
var label = new Label()
{
AutomationId = "HeaderLabel",
Text = "Flyout Header",
MinimumHeightRequest = 30,
};
FlyoutHeader = label;
FlyoutHeaderBehavior = FlyoutHeaderBehavior.CollapseOnScroll;
FlyoutIsPresented = true;
}
}

Просмотреть файл

@ -0,0 +1,23 @@
#if ANDROID
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue18443 : _IssuesUITest
{
public Issue18443(TestDevice device) : base(device) { }
public override string Issue => "SelectionLength Property Not Applied to Entry at Runtime";
[Test]
[Category(UITestCategories.Entry)]
public void EntrySelectionLengthRuntimeUpdate()
{
App.WaitForElement("entry");
VerifyScreenshot();
}
}
}
#endif

Просмотреть файл

@ -4,9 +4,9 @@ using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue186751 : _IssuesUITest
public class Issue18675 : _IssuesUITest
{
public Issue186751(TestDevice device) : base(device)
public Issue18675(TestDevice device) : base(device)
{
}

Просмотреть файл

@ -0,0 +1,29 @@
#if WINDOWS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue18702 : _IssuesUITest
{
const string element = "collectionView";
public Issue18702(TestDevice testDevice) : base(testDevice)
{
}
public override string Issue => "When the CollectionView has a group footer template it should not crash the application";
[Test]
[Category(UITestCategories.CollectionView)]
[Category(UITestCategories.Compatibility)]
[FailsOnIOS]
public void CollectionViewGroupFooterTemplateShouldNotCrash()
{
App.WaitForElement(element);
VerifyScreenshot();
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,27 @@
#if !MACCATALYST
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue19152 : _IssuesUITest
{
public Issue19152(TestDevice device): base(device)
{
}
public override string Issue => "Windows | Entry ClearButton not taking color of text";
[Test]
[Category(UITestCategories.Entry)]
public void EntryClearButtonColorShouldMatchTextColor()
{
App.WaitForElement("entry");
App.Tap("button");
VerifyScreenshot();
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,28 @@
#if IOS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue19500 : _IssuesUITest
{
public override string Issue => "[iOS] Editor is not be able to scroll if IsReadOnly is true";
public Issue19500(TestDevice device) : base(device)
{
}
[Test]
[Category(UITestCategories.Editor)]
public void TextInEditorShouldScroll()
{
_ = App.WaitForElement("editor");
App.ScrollDown("editor");
// The test passes if the text inside the editor scrolls down
VerifyScreenshot();
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,24 @@
using NUnit.Framework;
using NUnit.Framework.Legacy;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue24284 : _IssuesUITest
{
public Issue24284(TestDevice testDevice) : base(testDevice){ }
public override string Issue => "FlyoutHeaderAdaptsToMinimumHeight";
[Test]
[Category(UITestCategories.Shell)]
public void FlyoutHeaderAdaptsToMinimumHeight()
{
var heightReferenceLabel = App.WaitForElement("HeightReferenceLabel").GetRect();
var headerLabel = App.WaitForElement("HeaderLabel").GetRect();
ClassicAssert.True(Math.Abs(headerLabel.Height - heightReferenceLabel.Height) < 0.2);
}
}
}

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.2 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 87 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 36 KiB

Просмотреть файл

@ -42,6 +42,7 @@ namespace Microsoft.Maui.Handlers
// TODO: NET8 issoto - Change the return type to MauiAppCompatEditText
protected override void ConnectHandler(AppCompatEditText platformView)
{
platformView.ViewAttachedToWindow += OnPlatformViewAttachedToWindow;
platformView.TextChanged += OnTextChanged;
platformView.FocusChange += OnFocusedChange;
platformView.Touch += OnTouch;
@ -52,7 +53,7 @@ namespace Microsoft.Maui.Handlers
protected override void DisconnectHandler(AppCompatEditText platformView)
{
_clearButtonDrawable = null;
platformView.ViewAttachedToWindow -= OnPlatformViewAttachedToWindow;
platformView.TextChanged -= OnTextChanged;
platformView.FocusChange -= OnFocusedChange;
platformView.Touch -= OnTouch;
@ -146,6 +147,16 @@ namespace Microsoft.Maui.Handlers
handler.PlatformView.Focus(request);
}
void OnPlatformViewAttachedToWindow(object? sender, ViewAttachedToWindowEventArgs e)
{
if (PlatformView.IsAlive() && PlatformView.Enabled)
{
// https://issuetracker.google.com/issues/37095917
PlatformView.Enabled = false;
PlatformView.Enabled = true;
}
}
void OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (VirtualView == null)

Просмотреть файл

@ -68,6 +68,9 @@ namespace Microsoft.Maui.Platform
"TextControlForegroundPointerOver",
"TextControlForegroundFocused",
"TextControlForegroundDisabled",
"TextControlButtonForeground",
"TextControlButtonForegroundPointerOver",
"TextControlButtonForegroundPressed"
};
public static void UpdateCharacterSpacing(this TextBox textBox, ITextStyle textStyle)

Просмотреть файл

@ -70,12 +70,24 @@ namespace Microsoft.Maui.Platform
public static void UpdateIsReadOnly(this UITextView textView, IEditor editor)
{
textView.UserInteractionEnabled = !(editor.IsReadOnly || editor.InputTransparent);
textView.UpdateEditable(editor);
}
public static void UpdateIsEnabled(this UITextView textView, IEditor editor)
{
textView.Editable = editor.IsEnabled;
textView.UpdateEditable(editor);
}
internal static void UpdateEditable(this UITextView textView, IEditor editor)
{
var isEditable = editor.IsEnabled && !editor.IsReadOnly;
textView.Editable = isEditable;
// If the input accessory view is set, we need to hide it when the editor is read-only
// otherwise it will appear when the editor recieves focus.
if (textView.InputAccessoryView is {} view)
view.Hidden = !isEditable;
}
public static void UpdateKeyboard(this UITextView textView, IEditor editor)

Просмотреть файл

@ -514,7 +514,9 @@ namespace Microsoft.Maui.Platform
public static void UpdateInputTransparent(this UIView platformView, IViewHandler handler, IView view)
{
if (view is ITextInput textInput)
// Interaction should not be disabled for an editor if it is set as read-only
// because this prevents users from scrolling the content inside an editor.
if (view is not IEditor && view is ITextInput textInput)
{
platformView.UpdateInputTransparent(textInput.IsReadOnly, view.InputTransparent);
return;

Просмотреть файл

@ -256,7 +256,7 @@ namespace Microsoft.Maui.DeviceTests
GetNativeEditor(editorHandler).Font.PointSize;
bool GetNativeIsReadOnly(EditorHandler editorHandler) =>
!GetNativeEditor(editorHandler).UserInteractionEnabled;
!GetNativeEditor(editorHandler).Editable;
bool GetNativeIsTextPredictionEnabled(EditorHandler editorHandler) =>
GetNativeEditor(editorHandler).AutocorrectionType == UITextAutocorrectionType.Yes;