Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(droid): add workaround for NavView leaving blank on IsBackButtonVisible update #19517

Merged
merged 1 commit into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
using System.Threading.Tasks;
#if WINAPPSDK
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
using Uno.Extensions;
using Uno.Extensions.Specialized;
using Uno.UI.Extensions;
#elif __IOS__
using Uno.UI.RuntimeTests.Helpers;

using static Private.Infrastructure.TestServices;
using MUXC = Microsoft/* UWP don't rename */.UI.Xaml.Controls;

#if __IOS__
using UIKit;
#elif __MACOS__
using AppKit;
#else
#endif
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using static Private.Infrastructure.TestServices;
using Windows.UI;
using Microsoft.UI.Xaml.Media;
using Uno.UI.RuntimeTests.Helpers;

namespace Uno.UI.RuntimeTests.Tests.Microsoft_UI_Xaml_Controls
{
Expand All @@ -27,16 +33,16 @@
#endif
public async Task When_SelectedItem_Set_Before_Load_And_Theme_Changed()
{
var navView = new Microsoft/* UWP don't rename */.UI.Xaml.Controls.NavigationView()
var navView = new MUXC.NavigationView()
{
MenuItems =
{
new Microsoft/* UWP don't rename */.UI.Xaml.Controls.NavigationViewItem {Content = "Item 1"},
new Microsoft/* UWP don't rename */.UI.Xaml.Controls.NavigationViewItem {Content = "Item 2"},
new Microsoft/* UWP don't rename */.UI.Xaml.Controls.NavigationViewItem {Content = "Item 3"},
new MUXC.NavigationViewItem {Content = "Item 1"},
new MUXC.NavigationViewItem {Content = "Item 2"},
new MUXC.NavigationViewItem {Content = "Item 3"},
},
PaneDisplayMode = Microsoft/* UWP don't rename */.UI.Xaml.Controls.NavigationViewPaneDisplayMode.LeftMinimal,
IsBackButtonVisible = Microsoft/* UWP don't rename */.UI.Xaml.Controls.NavigationViewBackButtonVisible.Collapsed,
PaneDisplayMode = MUXC.NavigationViewPaneDisplayMode.LeftMinimal,
IsBackButtonVisible = MUXC.NavigationViewBackButtonVisible.Collapsed,
};
navView.SelectedItem = navView.MenuItems[1];
var hostGrid = new Grid() { MinWidth = 20, MinHeight = 20 };
Expand All @@ -53,9 +59,9 @@

await WindowHelper.WaitForIdle();

var togglePaneButton = navView.FindFirstChild<Button>(b => b.Name == "TogglePaneButton");
var icon = togglePaneButton?.FindFirstChild<FrameworkElement>(f => f.Name == "Icon");
var iconTextBlock = icon?.FindFirstChild<TextBlock>(includeCurrent: true);
var togglePaneButton = navView.FindFirstDescendant<Button>(b => b.Name == "TogglePaneButton");
var icon = togglePaneButton?.FindFirstDescendant<FrameworkElement>(f => f.Name == "Icon");
var iconTextBlock = icon as TextBlock ?? icon?.FindFirstDescendant<TextBlock>();

Assert.IsNotNull(iconTextBlock);

Expand All @@ -73,5 +79,103 @@
#endif
}
}

[TestMethod]
public async Task When_IsBackButtonVisible_Toggled()
{
// unoplatform/uno#19516
// droid-specific: There is a bug where setting IsBackButtonVisible would "lock" the size of all NVIs
// preventing resizing on items expansion/collapse.

var sut = new MUXC.NavigationView()
{
Height = 500,
IsBackButtonVisible = MUXC.NavigationViewBackButtonVisible.Collapsed,
IsPaneToggleButtonVisible = false,
PaneDisplayMode = MUXC.NavigationViewPaneDisplayMode.Left,
CompactModeThresholdWidth = 10,
ExpandedModeThresholdWidth = 50,
};
sut.ItemInvoked += (s, e) =>
{
// manual trigger for deepest/inner-most items
if (e.InvokedItemContainer is MUXC.NavigationViewItem nvi &&
nvi.MenuItems.Count == 0)
{
sut.IsBackButtonVisible = MUXC.NavigationViewBackButtonVisible.Visible;
}
};

var nvis = new Dictionary<string, MUXC.NavigationViewItem>();
//AddItems(sut.MenuItems, "", count: 4, depth: 1, maxDepth: 2);

Check warning on line 110 in src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NavigationView.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NavigationView.cs#L110

Remove this commented out code.
AddItems(sut.MenuItems, "", count: 3, depth: 1, maxDepth: 3);
void AddItems(IList<object> target, string prefix, int count, int depth, int maxDepth)
{
for (int i = 0; i < count; i++)
{
var header = prefix + (char)('A' + i);
var item = new MUXC.NavigationViewItem() { Content = header };

if (depth < maxDepth) AddItems(item.MenuItems, header, count, depth + 1, maxDepth);

Check failure on line 119 in src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NavigationView.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NavigationView.cs#L119

Add curly braces around the nested statement(s) in this 'if' block.

target.Add(item);
nvis.Add(header, item);
}
}

// for debugging
var panel = new StackPanel();
void AddTestButton(string label, Action action)
{
var button = new Button() { Content = label };
button.Click += (s, e) => action();
panel.Children.Add(button);
}
AddTestButton("InvalidateMeasure", () =>
{
foreach (var ir in sut.EnumerateDescendants().OfType<MUXC.ItemsRepeater>())
{
ir.InvalidateMeasure();
}
});
AddTestButton("IsBackButtonVisible toggle", () =>
sut.IsBackButtonVisible = sut.IsBackButtonVisible == MUXC.NavigationViewBackButtonVisible.Collapsed
? MUXC.NavigationViewBackButtonVisible.Visible
: MUXC.NavigationViewBackButtonVisible.Collapsed);
panel.Children.Add(sut);

await UITestHelper.Load(panel, x => x.IsLoaded);

var initialHeight = nvis["B"].ActualHeight;

nvis["B"].IsExpanded = true;
await UITestHelper.WaitForIdle();
var partiallyExpandedHeight = nvis["B"].ActualHeight;

nvis["BB"].IsExpanded = true;
await UITestHelper.WaitForIdle();
var fullyExpandedHeight = nvis["B"].ActualHeight;

// trigger the bug
await Task.Delay(2000); // necessary
sut.IsBackButtonVisible = MUXC.NavigationViewBackButtonVisible.Visible;
await UITestHelper.WaitForIdle();

nvis["BB"].IsExpanded = false;
await UITestHelper.WaitForIdle();
var partiallyCollapsedHeight = nvis["B"].ActualHeight;

nvis["B"].IsExpanded = false;
await UITestHelper.WaitForIdle();
var fullyCollapsedHeight = nvis["B"].ActualHeight;

// sanity check
Assert.IsTrue(initialHeight < partiallyExpandedHeight, $"Expanding 'B' should increase item 'B' height: {initialHeight} -> {partiallyExpandedHeight}");
Assert.IsTrue(partiallyExpandedHeight < fullyExpandedHeight, $"Expanding 'BB' should increase item 'B' height: {partiallyExpandedHeight} -> {fullyExpandedHeight}");

// verifying fix
Assert.IsTrue(fullyExpandedHeight > partiallyCollapsedHeight, $"Collapsing 'BB' should reduce item 'B' height: {fullyExpandedHeight} -> {partiallyCollapsedHeight}");
Assert.IsTrue(partiallyCollapsedHeight > fullyCollapsedHeight, $"Collapsing 'B' should reduce item 'B' height: {partiallyCollapsedHeight} -> {fullyCollapsedHeight}");
}
}
}
10 changes: 10 additions & 0 deletions src/Uno.UI/FeatureConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -929,5 +929,15 @@ public static bool IsEdgeToEdgeEnabled
}
}
#endif

public static class NavigationView
{
#if __ANDROID__
/// <summary>
/// Workaround for unoplatform/uno#19516 where toggling IsBackButtonVisible would stop NVIs from updating their layout/size when expanded/collapsed.
/// </summary>
public static bool EnableUno19516Workaround { get; set; } = true;
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Numerics;
using Microsoft/* UWP don't rename */.UI.Xaml.Automation.Peers;
using Microsoft/* UWP don't rename */.UI.Xaml.Controls.AnimatedVisuals;
using Uno.Disposables;
using Uno.Foundation.Logging;
using Uno.UI;
using Uno.UI.DataBinding;
using Uno.UI.Extensions;
using Uno.UI.Helpers.WinUI;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
Expand Down Expand Up @@ -4353,6 +4356,19 @@ private void OnPropertyChanged(DependencyPropertyChangedEventArgs args)
{
backButton.UpdateLayout();
}

#if __ANDROID__
// workaround for unoplatform/uno#19516 where toggling IsBackButtonVisible would stop NVIs from updating their layout/size when expanded/collapsed.
if (FeatureConfiguration.NavigationView.EnableUno19516Workaround &&
m_appliedTemplate && IsLoaded)
{
foreach (var ir in this.EnumerateDescendants().OfType<ItemsRepeater>())
{
ir.InvalidateMeasure();
}
}
#endif

UpdatePaneLayout();
}

Expand Down
Loading