Skip to content

Commit

Permalink
Fix setting bottom tab icon title (#12889) Fixes #12105
Browse files Browse the repository at this point in the history
* Don't reset entire menu when icon/title changes

* Auto-format source code

---------

Co-authored-by: GitHub Actions Autoformatter <autoformat@example.com>
  • Loading branch information
PureWeen and GitHub Actions Autoformatter committed Feb 15, 2023
1 parent e613b5b commit 16a3972
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Google.Android.Material.BottomSheet;
using Google.Android.Material.Navigation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Graphics;
using AColor = Android.Graphics.Color;
using AView = Android.Views.View;
Expand All @@ -38,7 +39,7 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
{
_shellAppearance = appearance;

if (appearance != null)
if (appearance is not null)
SetAppearance(appearance);
else
ResetAppearance();
Expand All @@ -59,6 +60,9 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
bool _appearanceSet;
public IShellItemController ShellItemController => ShellItem;
IMauiContext MauiContext => ShellContext.Shell.Handler.MauiContext;
IMenuItem _updateMenuItemTitle;
IMenuItem _updateMenuItemIcon;
ShellSection _updateMenuItemSource;

public ShellItemRenderer(IShellContext shellContext) : base(shellContext)
{
Expand All @@ -73,10 +77,10 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container,
_navigationArea = PlatformInterop.CreateNavigationBarArea(context, _outerLayout);
_bottomView = PlatformInterop.CreateNavigationBar(context, Resource.Attribute.bottomNavigationViewStyle, _outerLayout, this);

if (ShellItem == null)
if (ShellItem is null)
throw new InvalidOperationException("Active Shell Item not set. Have you added any Shell Items to your Shell?");

if (ShellItem.CurrentItem == null)
if (ShellItem.CurrentItem is null)
throw new InvalidOperationException($"Content not found for active {ShellItem}. Title: {ShellItem.Title}. Route: {ShellItem.Route}.");

HookEvents(ShellItem);
Expand All @@ -92,12 +96,12 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container,

void Destroy()
{
if (ShellItem != null)
if (ShellItem is not null)
UnhookEvents(ShellItem);

((IShellController)ShellContext?.Shell)?.RemoveAppearanceObserver(this);

if (_bottomSheetDialog != null)
if (_bottomSheetDialog is not null)
{
_bottomSheetDialog.DismissEvent -= OnMoreSheetDismissed;
_bottomSheetDialog?.Dispose();
Expand All @@ -108,7 +112,7 @@ void Destroy()
_appearanceTracker?.Dispose();
_outerLayout?.Dispose();

if (_bottomView != null)
if (_bottomView is not null)
{
_bottomView?.SetOnItemSelectedListener(null);
_bottomView?.Background?.Dispose();
Expand Down Expand Up @@ -143,9 +147,9 @@ public override void OnDestroy()

protected virtual void SetAppearance(ShellAppearance appearance)
{
if (_bottomView == null ||
if (_bottomView is null ||
_bottomView.Visibility == ViewStates.Gone ||
DisplayedPage == null)
DisplayedPage is null)
{
return;
}
Expand Down Expand Up @@ -229,7 +233,7 @@ void clickCallback(object s, EventArgs e)
(result) =>
{
image.SetImageDrawable(result?.Value);
if (result?.Value != null)
if (result?.Value is not null)
{
var color = Colors.Black.MultiplyAlpha(0.6f).ToPlatform();
result.Value.SetTint(color);
Expand Down Expand Up @@ -287,13 +291,13 @@ protected override void OnDisplayedPageChanged(Page newPage, Page oldPage)
{
base.OnDisplayedPageChanged(newPage, oldPage);

if (oldPage != null)
if (oldPage is not null)
oldPage.PropertyChanged -= OnDisplayedElementPropertyChanged;

if (newPage != null)
if (newPage is not null)
newPage.PropertyChanged += OnDisplayedElementPropertyChanged;

if (newPage != null && !_menuSetup)
if (newPage is not null && !_menuSetup)
{
SetupMenu();
}
Expand Down Expand Up @@ -358,7 +362,7 @@ protected virtual void OnMoreSheetDismissed(object sender, EventArgs e)
{
OnShellSectionChanged();

if (_bottomSheetDialog != null)
if (_bottomSheetDialog is not null)
{
_bottomSheetDialog.DismissEvent -= OnMoreSheetDismissed;
_bottomSheetDialog.Dispose();
Expand All @@ -377,24 +381,53 @@ protected override void OnShellSectionPropertyChanged(object sender, PropertyCha
{
base.OnShellSectionPropertyChanged(sender, e);

if (e.PropertyName == BaseShellItem.IsEnabledProperty.PropertyName)
if (e.IsOneOf(BaseShellItem.TitleProperty, BaseShellItem.IconProperty, BaseShellItem.IsEnabledProperty))
{
var content = (ShellSection)sender;
var index = ((IShellItemController)ShellItem).GetItems().IndexOf(content);
var shellSection = (ShellSection)sender;
var index = ((IShellItemController)ShellItem).GetItems().IndexOf(shellSection);

var itemCount = ((IShellItemController)ShellItem).GetItems().Count;
var maxItems = _bottomView.MaxItemCount;
IMenuItem menuItem = null;

if (itemCount > maxItems && index > maxItems - 2)
return;
if (!(itemCount > maxItems && index > maxItems - 2))
{
menuItem = _bottomView.Menu.FindItem(index);
}

var menuItem = _bottomView.Menu.FindItem(index);
UpdateShellSectionEnabled(content, menuItem);
}
else if (e.PropertyName == BaseShellItem.TitleProperty.PropertyName ||
e.PropertyName == BaseShellItem.IconProperty.PropertyName)
{
SetupMenu();
if (menuItem is not null)
{
if (e.Is(BaseShellItem.IsEnabledProperty))
{
UpdateShellSectionEnabled(shellSection, menuItem);
}
else if (e.Is(BaseShellItem.IconProperty))
{
_updateMenuItemIcon = menuItem;
}
else if (e.Is(BaseShellItem.TitleProperty))
{
_updateMenuItemTitle = menuItem;
}
}

// This is primarily used so that `SetupMenu` is still called when the
// title and icon property change calls happen. We don't want to break users
// that are dependent on that behavior
if (e.IsOneOf(BaseShellItem.IconProperty, BaseShellItem.TitleProperty))
{
try
{
_updateMenuItemSource = shellSection;
SetupMenu();
}
finally
{
_updateMenuItemIcon = null;
_updateMenuItemTitle = null;
_updateMenuItemSource = null;
}
}
}
}

Expand All @@ -406,7 +439,35 @@ protected virtual void OnTabReselected(ShellSection shellSection)

protected virtual void SetupMenu(IMenu menu, int maxBottomItems, ShellItem shellItem)
{
if (DisplayedPage == null)
if ((_updateMenuItemIcon is not null || _updateMenuItemTitle is not null) &&
_menuSetup &&
_updateMenuItemSource is not null &&
_bottomView.IsAlive() &&
_bottomView.IsAttachedToWindow)
{
if (_updateMenuItemIcon is not null)
{
var menuItem = _updateMenuItemIcon;
var shellSection = _updateMenuItemSource;
_updateMenuItemSource = null;
_updateMenuItemIcon = null;

UpdateShellSectionIcon(shellSection, menuItem);
return;
}
else if (_updateMenuItemTitle is not null)
{
var menuItem = _updateMenuItemTitle;
var shellSection = _updateMenuItemSource;
_updateMenuItemSource = null;
_updateMenuItemIcon = null;

UpdateShellSectionTitle(shellSection, menuItem);
return;
}
}

if (DisplayedPage is null)
return;

if (ShellItemController.ShowTabs)
Expand All @@ -427,6 +488,18 @@ protected virtual void SetupMenu(IMenu menu, int maxBottomItems, ShellItem shell
UpdateTabBarVisibility();
}

protected virtual void UpdateShellSectionIcon(ShellSection shellSection, IMenuItem menuItem)
{
BottomNavigationViewUtils.SetMenuItemIcon(menuItem, shellSection.Icon, MauiContext)
.FireAndForget(e => MauiContext?.CreateLogger<ShellItemRenderer>()?
.LogWarning(e, "Failed to Update Shell Section Icon"));
}

protected virtual void UpdateShellSectionTitle(ShellSection shellSection, IMenuItem menuItem)
{
BottomNavigationViewUtils.SetMenuItemTitle(menuItem, shellSection.Title);
}

protected virtual void UpdateShellSectionEnabled(ShellSection shellSection, IMenuItem menuItem)
{
bool tabEnabled = shellSection.IsEnabled;
Expand All @@ -453,12 +526,12 @@ void SetupMenu()

protected virtual void UpdateTabBarVisibility()
{
if (DisplayedPage == null)
if (DisplayedPage is null)
return;

_bottomView.Visibility = ShellItemController.ShowTabs ? ViewStates.Visible : ViewStates.Gone;

if (_shellAppearance != null && !_appearanceSet)
if (_shellAppearance is not null && !_appearanceSet)
{
SetAppearance(_shellAppearance);
}
Expand Down
69 changes: 51 additions & 18 deletions src/Controls/src/Core/Platform/Android/BottomNavigationViewUtils.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Android.Content;
using Android.Graphics.Drawables;
Expand Down Expand Up @@ -39,6 +40,29 @@ internal static void UpdateEnabled(bool tabEnabled, IMenuItem menuItem)
menuItem.SetEnabled(tabEnabled);
}

internal static Task SetupMenuItem(
(string title, ImageSource icon, bool tabEnabled) item,
IMenu menu,
int index,
int currentIndex,
BottomNavigationView bottomView,
IMauiContext mauiContext,
out IMenuItem menuItem)
{
Task returnValue;
using var title = new Java.Lang.String(item.title);
menuItem = menu.Add(0, index, 0, title);
returnValue = SetMenuItemIcon(menuItem, item.icon, mauiContext);
UpdateEnabled(item.tabEnabled, menuItem);
if (index == currentIndex)
{
menuItem.SetChecked(true);
bottomView.SelectedItemId = index;
}

return returnValue;
}

internal static async void SetupMenu(
IMenu menu,
int maxBottomItems,
Expand All @@ -48,29 +72,33 @@ internal static async void SetupMenu(
IMauiContext mauiContext)
{
Context context = mauiContext.Context;
menu.Clear();

while (items.Count < menu.Size())
{
menu.RemoveItem(menu.GetItem(menu.Size() - 1).ItemId);
}

int numberOfMenuItems = items.Count;
bool showMore = numberOfMenuItems > maxBottomItems;
int end = showMore ? maxBottomItems - 1 : numberOfMenuItems;


List<IMenuItem> menuItems = new List<IMenuItem>();
List<Task> loadTasks = new List<Task>();
for (int i = 0; i < end; i++)
{
var item = items[i];
using (var title = new Java.Lang.String(item.title))

IMenuItem menuItem;
if (i >= menu.Size())
loadTasks.Add(SetupMenuItem(item, menu, i, currentIndex, bottomView, mauiContext, out menuItem));
else
{
var menuItem = menu.Add(0, i, 0, title);
menuItems.Add(menuItem);
menuItem = menu.GetItem(i);
SetMenuItemTitle(menuItem, item.title);
loadTasks.Add(SetMenuItemIcon(menuItem, item.icon, mauiContext));
UpdateEnabled(item.tabEnabled, menuItem);
if (i == currentIndex)
{
menuItem.SetChecked(true);
bottomView.SelectedItemId = i;
}
}

menuItems.Add(menuItem);
}

if (showMore)
Expand All @@ -88,17 +116,20 @@ internal static async void SetupMenu(

if (loadTasks.Count > 0)
await Task.WhenAll(loadTasks);
}

foreach (var menuItem in menuItems)
menuItem.Dispose();
internal static void SetMenuItemTitle(IMenuItem menuItem, string title)
{
using var jTitle = new Java.Lang.String(title);
menuItem.SetTitle(jTitle);
}

static async Task SetMenuItemIcon(IMenuItem menuItem, ImageSource source, IMauiContext context)
internal static async Task SetMenuItemIcon(IMenuItem menuItem, ImageSource source, IMauiContext context)
{
if (!menuItem.IsAlive())
return;

if (source == null)
if (source is null)
return;

var services = context.Services;
Expand All @@ -109,8 +140,10 @@ static async Task SetMenuItemIcon(IMenuItem menuItem, ImageSource source, IMauiC
source,
context.Context);

if (menuItem.IsAlive() && result is not null)
menuItem.SetIcon(result.Value);
if (menuItem.IsAlive())
{
menuItem.SetIcon(result?.Value);
}
}

public static BottomSheetDialog CreateMoreBottomSheet(
Expand Down Expand Up @@ -212,7 +245,7 @@ public static void SetShiftMode(this BottomNavigationView bottomNavigationView,
try
{
var menuView = bottomNavigationView.GetChildAt(0) as BottomNavigationMenuView;
if (menuView == null)
if (menuView is null)
{
System.Diagnostics.Debug.WriteLine("Unable to find BottomNavigationMenuView");
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ override Microsoft.Maui.Controls.SearchBar.IsEnabledCore.get -> bool
*REMOVED*~static Microsoft.Maui.Controls.MessagingCenter.Subscribe<TSender>(object subscriber, string message, System.Action<TSender> callback, TSender source = null) -> void
*REMOVED*~static Microsoft.Maui.Controls.MessagingCenter.Unsubscribe<TSender, TArgs>(object subscriber, string message) -> void
*REMOVED*~static Microsoft.Maui.Controls.MessagingCenter.Unsubscribe<TSender>(object subscriber, string message) -> void
~virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer.UpdateShellSectionIcon(Microsoft.Maui.Controls.ShellSection shellSection, Android.Views.IMenuItem menuItem) -> void
~virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer.UpdateShellSectionTitle(Microsoft.Maui.Controls.ShellSection shellSection, Android.Views.IMenuItem menuItem) -> void
*REMOVED*~Microsoft.Maui.Controls.OpenGLView.On<T>() -> Microsoft.Maui.Controls.IPlatformElementConfiguration<T, Microsoft.Maui.Controls.OpenGLView>
*REMOVED*~Microsoft.Maui.Controls.OpenGLView.OnDisplay.get -> System.Action<Microsoft.Maui.Graphics.Rect>
*REMOVED*~Microsoft.Maui.Controls.OpenGLView.OnDisplay.set -> void
Expand All @@ -71,4 +73,4 @@ override Microsoft.Maui.Controls.SearchBar.IsEnabledCore.get -> bool
*REMOVED*Microsoft.Maui.Controls.Application.SavePropertiesAsync() -> System.Threading.Tasks.Task!
*REMOVED*Microsoft.Maui.Controls.Application.Properties.get -> System.Collections.Generic.IDictionary<string!, object!>!
Microsoft.Maui.Controls.IValueConverter.Convert(object? value, System.Type! targetType, object? parameter, System.Globalization.CultureInfo! culture) -> object?
Microsoft.Maui.Controls.IValueConverter.ConvertBack(object? value, System.Type! targetType, object? parameter, System.Globalization.CultureInfo! culture) -> object?
Microsoft.Maui.Controls.IValueConverter.ConvertBack(object? value, System.Type! targetType, object? parameter, System.Globalization.CultureInfo! culture) -> object?
Loading

0 comments on commit 16a3972

Please sign in to comment.