File: Compatibility\iOS\Extensions\ToolbarItemExtensions.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
#nullable disable
using System;
using System.ComponentModel;
using CoreGraphics;
using Microsoft.Maui.Graphics;
using ObjCRuntime;
using UIKit;
using PointF = CoreGraphics.CGPoint;
using RectangleF = CoreGraphics.CGRect;
using SizeF = CoreGraphics.CGSize;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
{
	public static class ToolbarItemExtensions
	{
		public static UIKit.UIBarButtonItem ToUIBarButtonItem(this Microsoft.Maui.Controls.ToolbarItem item, bool forceName)
		{
			return ToUIBarButtonItem(item, false, false);
		}
 
		public static UIBarButtonItem ToUIBarButtonItem(this ToolbarItem item, bool forceName = false, bool forcePrimary = false)
		{
			if (item.Order == ToolbarItemOrder.Secondary && !forcePrimary)
				return new SecondaryToolbarItem(item);
			return new PrimaryToolbarItem(item, forceName);
		}
 
		sealed class PrimaryToolbarItem : UIBarButtonItem
		{
			readonly bool _forceName;
			readonly WeakReference<ToolbarItem> _item;
 
			public PrimaryToolbarItem(ToolbarItem item, bool forceName)
			{
				_forceName = forceName;
				_item = new(item);
 
				if (item.IconImageSource != null && !item.IconImageSource.IsEmpty && !forceName)
					UpdateIconAndStyle(item);
				else
					UpdateTextAndStyle(item);
				UpdateIsEnabled(item);
 
				Clicked += OnClicked;
				item.PropertyChanged += OnPropertyChanged;
 
				if (item != null && !string.IsNullOrEmpty(item.AutomationId))
					AccessibilityIdentifier = item.AutomationId;
 
				this.SetAccessibilityHint(item);
				this.SetAccessibilityLabel(item);
			}
 
			void OnClicked(object sender, EventArgs e)
			{
				if (_item.TryGetTarget(out var item))
				{
					((IMenuItemController)item).Activate();
				}
			}
 
			protected override void Dispose(bool disposing)
			{
				if (disposing && _item.TryGetTarget(out var item))
					item.PropertyChanged -= OnPropertyChanged;
				base.Dispose(disposing);
			}
 
			void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
			{
				if (!_item.TryGetTarget(out var item))
					return;
 
				if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName)
					UpdateIsEnabled(item);
				else if (e.PropertyName == MenuItem.TextProperty.PropertyName)
				{
					if (item.IconImageSource == null || item.IconImageSource.IsEmpty || _forceName)
						UpdateTextAndStyle(item);
				}
				else if (e.PropertyName == MenuItem.IconImageSourceProperty.PropertyName)
				{
					if (!_forceName)
					{
						if (item.IconImageSource != null && !item.IconImageSource.IsEmpty)
							UpdateIconAndStyle(item);
						else
							UpdateTextAndStyle(item);
					}
				}
#pragma warning disable CS0618 // Type or member is obsolete
				else if (e.PropertyName == AutomationProperties.HelpTextProperty.PropertyName)
					this.SetAccessibilityHint(item);
				else if (e.PropertyName == AutomationProperties.NameProperty.PropertyName)
					this.SetAccessibilityLabel(item);
#pragma warning restore CS0618 // Type or member is obsolete
			}
 
			void UpdateIconAndStyle(ToolbarItem item)
			{
				if (item?.IconImageSource == null)
				{
					Image = null;
					Style = UIBarButtonItemStyle.Plain;
				}
				else
				{
					item.IconImageSource.LoadImage(item.FindMauiContext(), result =>
					{
						Image = result?.Value;
						Style = UIBarButtonItemStyle.Plain;
						if (item.IconImageSource is FontImageSource fontImageSource && fontImageSource.Color is not null)
						{
							TintColor = fontImageSource.Color.ToPlatform();
						}
					});
				}
			}
 
			void UpdateIsEnabled(ToolbarItem item)
			{
				Enabled = item.IsEnabled;
			}
 
			void UpdateTextAndStyle(ToolbarItem item)
			{
				Title = item.Text;
#pragma warning disable CA1416, CA1422 // TODO: [UnsupportedOSPlatform("ios8.0")]
				Style = UIBarButtonItemStyle.Bordered;
#pragma warning restore CA1416, CA1422
				Image = null;
			}
		}
 
		sealed class SecondaryToolbarItem : UIBarButtonItem
		{
			readonly WeakReference<ToolbarItem> _item;
 
			public SecondaryToolbarItem(ToolbarItem item) : base(new SecondaryToolbarItemContent())
			{
				_item = new(item);
				UpdateText(item);
				UpdateIcon(item);
				UpdateIsEnabled(item);
 
				((SecondaryToolbarItemContent)CustomView).TouchUpInside += OnClicked;
				item.PropertyChanged += OnPropertyChanged;
 
				if (item != null && !string.IsNullOrEmpty(item.AutomationId))
					AccessibilityIdentifier = item.AutomationId;
 
				this.SetAccessibilityHint(item);
				this.SetAccessibilityLabel(item);
			}
 
			void OnClicked(object sender, EventArgs e)
			{
				if (_item.TryGetTarget(out var item))
				{
					((IMenuItemController)item).Activate();
				}
			}
 
			protected override void Dispose(bool disposing)
			{
				if (disposing && _item.TryGetTarget(out var item))
					item.PropertyChanged -= OnPropertyChanged;
				base.Dispose(disposing);
			}
 
			void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
			{
				if (!_item.TryGetTarget(out var item))
					return;
 
				if (e.PropertyName == MenuItem.TextProperty.PropertyName)
					UpdateText(item);
				else if (e.PropertyName == MenuItem.IconImageSourceProperty.PropertyName)
					UpdateIcon(item);
				else if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName)
					UpdateIsEnabled(item);
#pragma warning disable CS0618 // Type or member is obsolete
				else if (e.PropertyName == AutomationProperties.HelpTextProperty.PropertyName)
					this.SetAccessibilityHint(item);
				else if (e.PropertyName == AutomationProperties.NameProperty.PropertyName)
#pragma warning restore CS0618 // Type or member is obsolete
					this.SetAccessibilityLabel(item);
			}
 
			void UpdateIcon(ToolbarItem item)
			{
				if (item.IconImageSource != null && !item.IconImageSource.IsEmpty)
				{
					item.IconImageSource.LoadImage(item.FindMauiContext(), result =>
					{
						((SecondaryToolbarItemContent)CustomView).Image = result?.Value;
					});
				}
				else
				{
					((SecondaryToolbarItemContent)CustomView).Image = null;
				}
			}
 
			void UpdateIsEnabled(ToolbarItem item)
			{
				((UIControl)CustomView).Enabled = item.IsEnabled;
			}
 
			void UpdateText(ToolbarItem item)
			{
				((SecondaryToolbarItemContent)CustomView).Text = item.Text;
			}
 
			sealed class SecondaryToolbarItemContent : UIControl
			{
				readonly UIImageView _imageView;
				readonly UILabel _label;
 
				public SecondaryToolbarItemContent()
				{
					BackgroundColor = UIColor.Clear;
					_imageView = new UIImageView { BackgroundColor = UIColor.Clear };
					AddSubview(_imageView);
 
					_label = new UILabel { BackgroundColor = UIColor.Clear, Lines = 1, LineBreakMode = UILineBreakMode.TailTruncation, Font = UIFont.SystemFontOfSize(10) };
					AddSubview(_label);
				}
 
				public override bool Enabled
				{
					get { return base.Enabled; }
					set
					{
						base.Enabled = value;
						_label.Enabled = value;
						_imageView.Alpha = value ? 1f : 0.25f;
					}
				}
 
				public UIImage Image
				{
					get { return _imageView.Image; }
					set { _imageView.Image = value; }
				}
 
				public string Text
				{
					get { return _label.Text; }
					set { _label.Text = value; }
				}
 
				public override void LayoutSubviews()
				{
					base.LayoutSubviews();
 
					const float padding = 5f;
					var imageSize = _imageView.SizeThatFitsImage(Bounds.Size);
					var fullStringSize = _label.SizeThatFits(Bounds.Size);
 
					if (imageSize.Width > 0 && (string.IsNullOrEmpty(Text) || fullStringSize.Width > Bounds.Width / 3))
					{
						_imageView.Frame = new RectangleF(PointF.Empty, imageSize);
						_imageView.Center = new PointF(Bounds.GetMidX(), Bounds.GetMidY());
						_label.Hidden = true;
						return;
					}
 
					_label.Hidden = false;
					var availableWidth = Bounds.Width - padding * 3 - imageSize.Width;
					var stringSize = _label.SizeThatFits(new SizeF(availableWidth, Bounds.Height - padding * 2));
 
					availableWidth = Bounds.Width;
					availableWidth -= stringSize.Width;
					availableWidth -= imageSize.Width;
 
					var x = availableWidth / 2;
 
					var frame = new RectangleF(new PointF(x, Bounds.GetMidY() - imageSize.Height / 2), imageSize);
					_imageView.Frame = frame;
 
					frame.X = frame.Right + (imageSize.Width > 0 ? padding : 0);
					frame.Size = stringSize;
					frame.Height = Bounds.Height;
					frame.Y = 0;
					_label.Frame = frame;
				}
			}
		}
	}
}