diff --git a/src/Compatibility/ControlGallery/src/Core/CoreGalleryPages/ListViewCoreGalleryPage.cs b/src/Compatibility/ControlGallery/src/Core/CoreGalleryPages/ListViewCoreGalleryPage.cs index 0dd97fef9375..209d862173c0 100644 --- a/src/Compatibility/ControlGallery/src/Core/CoreGalleryPages/ListViewCoreGalleryPage.cs +++ b/src/Compatibility/ControlGallery/src/Core/CoreGalleryPages/ListViewCoreGalleryPage.cs @@ -175,7 +175,7 @@ public ListViewViewModel() new Employee ("Andrew 3", TimeSpan.FromDays (1000), 60), }; - Enumerable.Range(0, 9000).Select(e => new Employee(e.ToString(), TimeSpan.FromDays(1), 60)).ForEach(e => Employees.Add(e)); + Enumerable.Range(0, 9000).Select(e => new Employee(e.ToString(), TimeSpan.FromDays(1), 60)).ToList().ForEach(e => Employees.Add(e)); } } diff --git a/src/Compatibility/ControlGallery/src/Core/GalleryPages/GroupedListActionsGallery.cs b/src/Compatibility/ControlGallery/src/Core/GalleryPages/GroupedListActionsGallery.cs index 4ff9be6a7c25..417f9f6db296 100644 --- a/src/Compatibility/ControlGallery/src/Core/GalleryPages/GroupedListActionsGallery.cs +++ b/src/Compatibility/ControlGallery/src/Core/GalleryPages/GroupedListActionsGallery.cs @@ -67,6 +67,17 @@ void OnPropertyChanged([CallerMemberName] string propertyName = null) if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } + + public int IndexOf(Func selector) + { + for (int i = 0; i < this.Count; i++) + { + if (selector(this[i])) + return i; + } + + return -1; + } } class GroupAction @@ -139,11 +150,25 @@ public Group Parent }) }; - readonly ObservableList _groups; + readonly GroupObservableList _groups; + + class GroupObservableList : ObservableList + { + public int IndexOf(Func selector) + { + for (int i = 0; i < this.Count; i++) + { + if (selector(this[i])) + return i; + } + + return -1; + } + } - ObservableList CreateItemSource() + GroupObservableList CreateItemSource() { - return new ObservableList { + return new GroupObservableList { new Group ("General") { new GroupAction ("Change group name", ga => ga.Parent.Name += " (changed)"), new GroupAction ("Change group short name", ga => ga.Parent.ShortName = ga.Parent.Name[1].ToString()) diff --git a/src/Compatibility/ControlGallery/src/Core/GalleryPages/MapGallery.xaml.cs b/src/Compatibility/ControlGallery/src/Core/GalleryPages/MapGallery.xaml.cs index 4947583047c4..6f16d9ffb389 100644 --- a/src/Compatibility/ControlGallery/src/Core/GalleryPages/MapGallery.xaml.cs +++ b/src/Compatibility/ControlGallery/src/Core/GalleryPages/MapGallery.xaml.cs @@ -23,7 +23,7 @@ public MapGallery() InitializeComponent(); Map = MakeMap(); - Map.Pins.ForEach(pin => + Map.Pins.ToList().ForEach(pin => { pin.MarkerClicked += MarkerClicked; pin.InfoWindowClicked += InfoWindowClicked; diff --git a/src/Compatibility/ControlGallery/src/Core/GalleryPages/PlatformSpecificsGalleries/WindowsPlatformSpecificsGalleryHelpers.cs b/src/Compatibility/ControlGallery/src/Core/GalleryPages/PlatformSpecificsGalleries/WindowsPlatformSpecificsGalleryHelpers.cs index 53795dd78bdc..3ce216781552 100644 --- a/src/Compatibility/ControlGallery/src/Core/GalleryPages/PlatformSpecificsGalleries/WindowsPlatformSpecificsGalleryHelpers.cs +++ b/src/Compatibility/ControlGallery/src/Core/GalleryPages/PlatformSpecificsGalleries/WindowsPlatformSpecificsGalleryHelpers.cs @@ -57,7 +57,7 @@ public static Layout CreateChanger(Type enumType, string defaultOption, Action

{ diff --git a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2680ScrollView.cs b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2680ScrollView.cs index d4296b8f84f3..6796c6cbe991 100644 --- a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2680ScrollView.cs +++ b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2680ScrollView.cs @@ -43,7 +43,7 @@ protected override void Init() AutomationId = FirstItemMark }); Enumerable.Range(2, 50).Select(i => new Label { Text = $"Test label {i}" }) - .ForEach(label => longStackLayout.Children.Add(label)); + .ToList().ForEach(label => longStackLayout.Children.Add(label)); scrollView = new ScrollView { diff --git a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2976.cs b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2976.cs index 1a815460bf6d..ac4efd2b2855 100644 --- a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2976.cs +++ b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue2976.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Microsoft.Maui.Controls.CustomAttributes; using Microsoft.Maui.Controls.Internals; +using System.Linq; #if UITEST using Xamarin.UITest; @@ -111,7 +112,7 @@ public void NotifyItemSelected(object item) { if (ItemSelected != null) - ItemSelected(this, new SelectedItemChangedEventArgs(item, Items?.IndexOf(item) ?? -1)); + ItemSelected(this, new SelectedItemChangedEventArgs(item, Items?.ToList().IndexOf($"{item}") ?? -1)); } public NativeListView() @@ -337,7 +338,7 @@ public void NotifyItemSelected(object item) { if (ItemSelected != null) - ItemSelected(this, new SelectedItemChangedEventArgs(item, Items?.IndexOf(item) ?? -1)); + ItemSelected(this, new SelectedItemChangedEventArgs(item, Items?.ToList().IndexOf((DataSource)item) ?? -1)); } public NativeListView2() diff --git a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue3089.cs b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue3089.cs index ac87544fb53b..a5f3129fa8aa 100644 --- a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue3089.cs +++ b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue3089.cs @@ -33,7 +33,7 @@ protected override void Init() { var oc = new ObservableCollection(new[] { $"Click {reload}", "and this text should go away" }); - Enumerable.Range(0, 100).ForEach(x => oc.Add(x.ToString())); + Enumerable.Range(0, 100).ToList().ForEach(x => oc.Add(x.ToString())); PushAsync(new MainPageCode { diff --git a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue4597.cs b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue4597.cs index d1dd63b462e4..88e77e60e95f 100644 --- a/src/Compatibility/ControlGallery/src/Issues.Shared/Issue4597.cs +++ b/src/Compatibility/ControlGallery/src/Issues.Shared/Issue4597.cs @@ -149,7 +149,7 @@ protected override void Init() Command = new Command(() => { var activeImage = layout.Children.Last(); - int nextIndex = imageControls.IndexOf(activeImage) + 1; + int nextIndex = imageControls.ToList().IndexOf(activeImage) + 1; if(nextIndex >= imageControls.Length) nextIndex = 0; diff --git a/src/Compatibility/ControlGallery/src/Issues.Shared/RefreshViewTests.cs b/src/Compatibility/ControlGallery/src/Issues.Shared/RefreshViewTests.cs index 189fb33c5be8..0ddb151cd1e7 100644 --- a/src/Compatibility/ControlGallery/src/Issues.Shared/RefreshViewTests.cs +++ b/src/Compatibility/ControlGallery/src/Issues.Shared/RefreshViewTests.cs @@ -35,6 +35,7 @@ protected override void Init() Enumerable .Range(0, 10) .Select(_ => new Label() { HeightRequest = 200, Text = "Pull me down to refresh me" }) + .ToList() .ForEach(x => scrollViewContent.Children.Add(x)); diff --git a/src/Compatibility/Core/src/iOS/Extensions/ArrayExtensions.cs b/src/Compatibility/Core/src/iOS/Extensions/ArrayExtensions.cs deleted file mode 100644 index 4838cd73e1e3..000000000000 --- a/src/Compatibility/Core/src/iOS/Extensions/ArrayExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Microsoft.Maui.Controls.Internals; - -namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS -{ - internal static class ArrayExtensions - { - public static T[] Insert(this T[] self, int index, T item) - { - var result = new T[self.Length + 1]; - if (index > 0) - Array.Copy(self, result, index); - - result[index] = item; - - if (index < self.Length) - Array.Copy(self, index, result, index + 1, result.Length - index - 1); - - return result; - } - - public static T[] Remove(this T[] self, T item) - { - return self.RemoveAt(self.IndexOf(item)); - } - - public static T[] RemoveAt(this T[] self, int index) - { - var result = new T[self.Length - 1]; - if (index > 0) - Array.Copy(self, result, index); - - if (index < self.Length - 1) - Array.Copy(self, index + 1, result, index, self.Length - index - 1); - - return result; - } - } -} \ No newline at end of file diff --git a/src/Controls/src/Core/EnumerableExtensions.cs b/src/Controls/src/Core/EnumerableExtensions.cs index 811fd7fe1911..8f4e8ea28ced 100644 --- a/src/Controls/src/Core/EnumerableExtensions.cs +++ b/src/Controls/src/Core/EnumerableExtensions.cs @@ -59,60 +59,5 @@ public static IEnumerable GetGesturesFor(this IEnumerable(this IEnumerable enumeration, Action action) - { - foreach (T item in enumeration) - { - action(item); - } - } - - public static IDictionary> GroupToDictionary(this IEnumerable enumeration, Func func) - { - var result = new Dictionary>(); - foreach (TSource item in enumeration) - { - var group = func(item); - if (!result.ContainsKey(group)) - result.Add(group, new List { item }); - else - result[group].Add(item); - } - return result; - } - - public static int IndexOf(this IEnumerable enumerable, T item) - { - if (enumerable == null) - throw new ArgumentNullException("enumerable"); - - var i = 0; - foreach (T element in enumerable) - { - if (Equals(element, item)) - return i; - - i++; - } - - return -1; - } - - public static int IndexOf(this IEnumerable enumerable, Func predicate) - { - var i = 0; - foreach (T element in enumerable) - { - if (predicate(element)) - return i; - - i++; - } - - return -1; - } - - public static T Last(this IList self) => self[self.Count - 1]; } } \ No newline at end of file diff --git a/src/Controls/src/Core/HandlerImpl/NavigationPage.Impl.cs b/src/Controls/src/Core/HandlerImpl/NavigationPage.Impl.cs index 37d0b2054100..846725df718f 100644 --- a/src/Controls/src/Core/HandlerImpl/NavigationPage.Impl.cs +++ b/src/Controls/src/Core/HandlerImpl/NavigationPage.Impl.cs @@ -1,16 +1,33 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Graphics; using Microsoft.Maui.Layouts; namespace Microsoft.Maui.Controls { - public partial class NavigationPage : IView + public partial class NavigationPage : INavigationView { Thickness IView.Margin => Thickness.Zero; + partial void Init() + { + PushRequested += (_, args) => + { + var request = new MauiNavigationRequestedEventArgs(args.Page, args.BeforePage, args.Animated); + Handler?.Invoke(nameof(INavigationView.PushAsync), request); + + }; + + PopRequested += (_, args) => + { + var request = new MauiNavigationRequestedEventArgs(args.Page, args.BeforePage, args.Animated); + Handler?.Invoke(nameof(INavigationView.PopAsync), request); + }; + } + protected override Size MeasureOverride(double widthConstraint, double heightConstraint) { if (Content is IFrameworkElement frameworkElement) @@ -35,8 +52,60 @@ protected override Size ArrangeOverride(Rectangle bounds) return Frame.Size; } + void INavigationView.InsertPageBefore(IView page, IView before) + { + throw new NotImplementedException(); + } + + Task INavigationView.PopAsync() => + (this as INavigationView).PopAsync(true); + + async Task INavigationView.PopAsync(bool animated) + { + var thing = await this.PopAsync(animated); + return thing; + } + + Task INavigationView.PopModalAsync() + { + throw new NotImplementedException(); + } + + Task INavigationView.PopModalAsync(bool animated) + { + throw new NotImplementedException(); + } + + Task INavigationView.PushAsync(IView page) => + (this as INavigationView).PushAsync(page, true); + + Task INavigationView.PushAsync(IView page, bool animated) + { + return this.PushAsync((Page)page, animated); + } + + Task INavigationView.PushModalAsync(IView page) + { + throw new NotImplementedException(); + } + + Task INavigationView.PushModalAsync(IView page, bool animated) + { + throw new NotImplementedException(); + } + + void INavigationView.RemovePage(IView page) + { + throw new NotImplementedException(); + } + IFrameworkElement Content => this.CurrentPage; + + IReadOnlyList INavigationView.ModalStack => throw new NotImplementedException(); + + IReadOnlyList INavigationView.NavigationStack => + this.Navigation.NavigationStack; } } diff --git a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Android.cs b/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Android.cs deleted file mode 100644 index a0e95aa9a9f7..000000000000 --- a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Android.cs +++ /dev/null @@ -1,277 +0,0 @@ -#nullable enable - -using System; -using System.Collections.Generic; -using Android.Runtime; -using Android.Views; -using AndroidX.AppCompat.Widget; -using AndroidX.Navigation; -using AndroidX.Navigation.Fragment; -using AndroidX.Navigation.UI; -using Google.Android.Material.AppBar; -using Microsoft.Maui.Controls.Internals; -using Microsoft.Maui.Controls.Platform; -using Microsoft.Maui.Handlers; -using AView = Android.Views.View; - -namespace Microsoft.Maui.Controls.Handlers -{ - public partial class NavigationPageHandler : - ViewHandler - //, IManageFragments, IOnClickListener, ILifeCycleState - { - private NavHostFragment? _navHost; - private FragmentNavigator? _fragmentNavigator; - private Toolbar? _toolbar; - private AppBarLayout? _appBar; - - NavHostFragment NavHost - { - get => _navHost ?? throw new InvalidOperationException($"NavHost cannot be null"); - set => _navHost = value; - } - - FragmentNavigator FragmentNavigator - { - get => _fragmentNavigator ?? throw new InvalidOperationException($"FragmentNavigator cannot be null"); - set => _fragmentNavigator = value; - } - - int NativeNavigationStackCount => NavHost?.NavController.BackStack.Size() - 1 ?? 0; - int NavigationStackCount => VirtualView?.Navigation.NavigationStack.Count ?? 0; - - internal Toolbar Toolbar - { - get => _toolbar ?? throw new InvalidOperationException($"ToolBar cannot be null"); - set => _toolbar = value; - } - - internal AppBarLayout AppBar - { - get => _appBar ?? throw new InvalidOperationException($"AppBar cannot be null"); - set => _appBar = value; - } - - protected override AView CreateNativeView() - { - LayoutInflater? li = LayoutInflater.From(Context); - _ = li ?? throw new InvalidOperationException($"LayoutInflater cannot be null"); - - var view = li.Inflate(Resource.Layout.navigationlayout, null).JavaCast(); - _ = view ?? throw new InvalidOperationException($"Resource.Layout.navigationlayout view not found"); - - _toolbar = view.FindViewById(Resource.Id.maui_toolbar); - _appBar = view.FindViewById(Resource.Id.appbar); - return view; - } - - protected override void ConnectHandler(AView nativeView) - { - var fragmentManager = Context.GetFragmentManager(); - _ = fragmentManager ?? throw new InvalidOperationException($"GetFragmentManager returned null"); - _ = VirtualView ?? throw new InvalidOperationException($"VirtualView cannot be null"); - - NavHost = (NavHostFragment) - fragmentManager.FindFragmentById(Resource.Id.nav_host); - - FragmentNavigator = - (FragmentNavigator)NavHost - .NavController - .NavigatorProvider - .GetNavigator(Java.Lang.Class.FromType(typeof(FragmentNavigator))); - - - var navGraphNavigator = - (NavGraphNavigator)NavHost - .NavController - .NavigatorProvider - .GetNavigator(Java.Lang.Class.FromType(typeof(NavGraphNavigator))); - - base.ConnectHandler(nativeView); - - INavigationPageController navController = VirtualView; - navController.PushRequested += OnPushed; - navController.PopRequested += OnPopped; - var inflater = NavHost.NavController.NavInflater; - NavGraph graph = new NavGraph(navGraphNavigator); - - NavDestination navDestination; - List destinations = new List(); - foreach (var page in VirtualView.Navigation.NavigationStack) - { - navDestination = - MauiFragmentNavDestination. - AddDestination( - page, - this, - graph, - FragmentNavigator); - - destinations.Add(navDestination.Id); - } - - graph.StartDestination = destinations[0]; - - NavHost.NavController.SetGraph(graph, null); - - for (var i = NativeNavigationStackCount; i < NavigationStackCount; i++) - { - var dest = destinations[i]; - NavHost.NavController.Navigate(dest); - } - } - - protected override void DisconnectHandler(AView nativeView) - { - base.DisconnectHandler(nativeView); - var navController = (INavigationPageController)VirtualView; - navController.PushRequested -= OnPushed; - navController.PopRequested -= OnPopped; - } - - void OnPushed(object? sender, NavigationRequestedEventArgs e) - { - var destination = - MauiFragmentNavDestination.AddDestination(e.Page, this, NavHost.NavController.Graph, FragmentNavigator); - - NavHost.NavController.Navigate(destination.Id, null); - } - - internal void OnPop() - { - VirtualView - .Navigation - .PopAsync() - .FireAndForget((e) => - { - Log.Warning(nameof(NavigationPageHandler), $"{e}"); - }); - } - - void OnPopped(object? sender, NavigationRequestedEventArgs e) - { - NavHost.NavController.NavigateUp(); - } - - void UpdatePadding() - { - } - - void UpdateBarTextColor() - { - UpdateBarBackground(); - } - - void UpdateBarBackground() - { - var context = Context; - var bar = Toolbar; - //ActionBarDrawerToggle toggle = _drawerToggle; - - if (bar == null) - return; - - //bool isNavigated = NavigationPageController.StackDepth > 1; - //bar.NavigationIcon = null; - var navPage = VirtualView; - - //if (isNavigated) - //{ - // if (NavigationPage.GetHasBackButton(currentPage) && !Context.IsDesignerContext()) - // { - // if (toggle != null) - // { - // toggle.DrawerIndicatorEnabled = false; - // toggle.SyncState(); - // } - - // var activity = (AppCompatActivity)context.GetActivity(); - // var icon = new DrawerArrowDrawable(activity.SupportActionBar.ThemedContext); - // icon.Progress = 1; - // bar.NavigationIcon = icon; - - // var prevPage = Element.Peek(1); - // var backButtonTitle = NavigationPage.GetBackButtonTitle(prevPage); - // _defaultNavigationContentDescription = backButtonTitle != null - // ? bar.SetNavigationContentDescription(prevPage, backButtonTitle) - // : bar.SetNavigationContentDescription(prevPage, _defaultNavigationContentDescription); - // } - // else if (toggle != null && _flyoutPage != null) - // { - // toggle.DrawerIndicatorEnabled = _flyoutPage.ShouldShowToolbarButton(); - // toggle.SyncState(); - // } - //} - //else - //{ - // if (toggle != null && _flyoutPage != null) - // { - // toggle.DrawerIndicatorEnabled = _flyoutPage.ShouldShowToolbarButton(); - // toggle.SyncState(); - // } - //} - - var tintColor = navPage.BarBackgroundColor; - Brush barBackground = navPage.BarBackground; - - if (barBackground == null && tintColor != null) - barBackground = new SolidColorBrush(tintColor); - - if (barBackground != null) - bar.UpdateBackground(barBackground); - //else if (tintColor == null) - // bar.BackgroundTintMode = null; - //else - //{ - // bar.Background = null; - // bar.BackgroundTintMode = PorterDuff.Mode.Src; - // bar.BackgroundTintList = ColorStateList.ValueOf(tintColor.ToNative()); - //} - - var textColor = navPage.BarTextColor; - if (textColor != null) - bar.SetTitleTextColor(textColor.ToNative().ToArgb()); - - //var navIconColor = NavigationPage.GetIconColor(currentPage); - //if (navIconColor != null && bar.NavigationIcon != null) - // DrawableExtensions.SetColorFilter(bar.NavigationIcon, navIconColor, FilterMode.SrcAtop); - - bar.Title = navPage.CurrentPage?.Title ?? string.Empty; - - //if (_toolbar.NavigationIcon != null && textColor != null) - //{ - // var icon = _toolbar.NavigationIcon as DrawerArrowDrawable; - // if (icon != null) - // icon.Color = textColor.ToAndroid().ToArgb(); - //} - - //UpdateTitleIcon(); - - //UpdateTitleView(); - } - - void UpdateTitleIcon() - { - } - void UpdateTitleView() - { - } - - - public static void MapPadding(NavigationPageHandler handler, NavigationPage view) - => handler.UpdatePadding(); - - public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage view) - => handler.UpdateBarTextColor(); - - public static void MapBarBackground(NavigationPageHandler handler, NavigationPage view) - => handler.UpdateBarBackground(); - - // TODO MAUI: Task Based Mappers? - public static void MapTitleIcon(NavigationPageHandler handler, NavigationPage view) - => handler.UpdateTitleIcon(); - - public static void MapTitleView(NavigationPageHandler handler, NavigationPage view) - => handler.UpdateTitleView(); - } -} diff --git a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Standard.cs b/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Standard.cs deleted file mode 100644 index c8aff483fe58..000000000000 --- a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Standard.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.Maui.Handlers; - -namespace Microsoft.Maui.Controls.Handlers -{ - public partial class NavigationPageHandler : - ViewHandler - { - protected override object CreateNativeView() - { - throw new NotImplementedException(); - } - - public static void MapPadding(NavigationPageHandler handler, NavigationPage view) { } - - public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage view) { } - - public static void MapBarBackground(NavigationPageHandler handler, NavigationPage view) { } - - public static void MapTitleIcon(NavigationPageHandler handler, NavigationPage view) { } - - public static void MapTitleView(NavigationPageHandler handler, NavigationPage view) { } - } -} diff --git a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Windows.cs b/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Windows.cs deleted file mode 100644 index a478cd5c69e3..000000000000 --- a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.Windows.cs +++ /dev/null @@ -1,703 +0,0 @@ -#nullable enable - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Threading.Tasks; -using Windows.Devices.Input; -using Windows.UI.Input; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media.Animation; -using Windows.UI.Core; -using Microsoft.UI.Xaml.Data; -using Microsoft.Maui.Controls.Internals; -using static Microsoft.Maui.Controls.PlatformConfiguration.WindowsSpecific.Page; -using WBrush = Microsoft.UI.Xaml.Media.Brush; -using WImageSource = Microsoft.UI.Xaml.Media.ImageSource; -using Microsoft.Maui.Graphics; -using Microsoft.Maui.Handlers; -using Microsoft.Maui.Controls.Platform; - -namespace Microsoft.Maui.Controls.Handlers -{ - public partial class NavigationPageHandler : - ViewHandler, ITitleProvider, ITitleIconProvider, - ITitleViewProvider, IToolbarProvider, IToolBarForegroundBinder, IViewHandler - { - Page? _currentPage; - Page? _previousPage; - - FlyoutPage? _parentFlyoutPage; - TabbedPage? _parentTabbedPage; - bool _showTitle = true; - VisualElementTracker _tracker; - EntranceThemeTransition _transition; - bool _parentsLookedUp = false; - - public void BindForegroundColor(AppBar appBar) - { - SetAppBarForegroundBinding(appBar); - } - - public void BindForegroundColor(AppBarButton button) - { - SetAppBarForegroundBinding(button); - } - - protected VisualElementTracker Tracker - { - get { return _tracker; } - set - { - if (_tracker == value) - return; - - if (_tracker != null) - _tracker.Dispose(); - - _tracker = value; - } - } - - //public void Dispose() - //{ - // Dispose(true); - //} - - void SetAppBarForegroundBinding(FrameworkElement element) - { - element.SetBinding(Control.ForegroundProperty, - new Microsoft.UI.Xaml.Data.Binding { Path = new PropertyPath("TitleBrush"), Source = NativeView, RelativeSource = new RelativeSource { Mode = RelativeSourceMode.TemplatedParent } }); - } - - WBrush ITitleProvider.BarBackgroundBrush - { - set - { - NativeView.ToolbarBackground = value; - UpdateTitleOnParents(); - } - } - - WBrush ITitleProvider.BarForegroundBrush - { - set - { - NativeView.TitleBrush = value; - UpdateTitleOnParents(); - } - } - - bool ITitleProvider.ShowTitle - { - get { return _showTitle; } - set - { - if (_showTitle == value) - return; - - _showTitle = value; - UpdateTitleVisible(); - UpdateTitleOnParents(); - } - } - - public string Title - { - get { return _currentPage?.Title ?? String.Empty; } - - set { /*Not implemented but required by interface*/ } - } - - public WImageSource TitleIcon { get; set; } - - public View? TitleView - { - get - { - if (_currentPage == null) - return null; - - return NavigationPage.GetTitleView(_currentPage) as View; - } - set { /*Not implemented but required by interface*/ } - } - - Task IToolbarProvider.GetCommandBarAsync() - { - return ((IToolbarProvider)NativeView)?.GetCommandBarAsync() ?? - Task.FromResult((CommandBar?)null); - } - - public event EventHandler ElementChanged; - - public override Size GetDesiredSize(double widthConstraint, double heightConstraint) - { - if (VirtualView == null) - return Size.Zero; - - var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint); - IViewHandler childRenderer = VirtualView.CurrentPage.Handler; - FrameworkElement? child = childRenderer.NativeView as FrameworkElement; - if (child == null) - return Size.Zero; - - double oldWidth = child.Width; - double oldHeight = child.Height; - - child.Height = double.NaN; - child.Width = double.NaN; - - child.Measure(constraint); - var result = new Size(Math.Ceiling(child.DesiredSize.Width), Math.Ceiling(child.DesiredSize.Height)); - - child.Width = oldWidth; - child.Height = oldHeight; - - return result; - } - - protected override PageControl CreateNativeView() - { - return new PageControl(); - } - - public override void SetVirtualView(IView view) - { - base.SetVirtualView(view); - - if (view != null && !(view is NavigationPage)) - throw new ArgumentException("VirtualView must be a Page", nameof(view)); - - if (view is NavigationPage np && np != null && np.CurrentPage is null) - throw new InvalidOperationException( - "NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage."); - - } - - protected override void ConnectHandler(PageControl nativeView) - { - base.ConnectHandler(nativeView); - - var virtualView = VirtualView; - - nativeView.PointerPressed += OnPointerPressed; - nativeView.SizeChanged += OnNativeSizeChanged; - Tracker = new BackgroundTracker(Control.BackgroundProperty) - { - Element = virtualView, - Container = NativeView - }; - - SetPage(virtualView.CurrentPage, false, false); - - nativeView.Loaded += OnLoaded; - nativeView.Unloaded += OnUnloaded; - - nativeView.DataContext = virtualView.CurrentPage; - - // Move this somewhere else - LookupRelevantParents(); - - // Enforce consistency rules on toolbar (show toolbar if top-level page is Navigation Page) - nativeView.ShouldShowToolbar = _parentFlyoutPage == null && _parentTabbedPage == null; - if (_parentTabbedPage != null) - virtualView.Appearing += OnElementAppearing; - - virtualView.PushRequested += OnPushRequested; - virtualView.PopRequested += OnPopRequested; - virtualView.PopToRootRequested += OnPopToRootRequested; - virtualView.InternalChildren.CollectionChanged += OnChildrenChanged; - - if (!string.IsNullOrEmpty(virtualView.AutomationId)) - nativeView.SetValue(Microsoft.UI.Xaml.Automation.AutomationProperties.AutomationIdProperty, virtualView.AutomationId); - - PushExistingNavigationStack(); - } - - protected override void DisconnectHandler(PageControl nativeView) - { - base.DisconnectHandler(nativeView); - - if (VirtualView == null) - return; - - VirtualView.PushRequested -= OnPushRequested; - VirtualView.PopRequested -= OnPopRequested; - VirtualView.PopToRootRequested -= OnPopToRootRequested; - VirtualView.InternalChildren.CollectionChanged -= OnChildrenChanged; - nativeView.PointerPressed -= OnPointerPressed; - nativeView.SizeChanged -= OnNativeSizeChanged; - nativeView.Loaded -= OnLoaded; - nativeView.Unloaded -= OnUnloaded; - - VirtualView.SendDisappearing(); - - nativeView.PointerPressed -= OnPointerPressed; - nativeView.SizeChanged -= OnNativeSizeChanged; - nativeView.Loaded -= OnLoaded; - nativeView.Unloaded -= OnUnloaded; - - if (_parentTabbedPage != null) - VirtualView.Appearing -= OnElementAppearing; - - SetPage(null, false, true); - _previousPage = null; - - if (_parentTabbedPage != null) - _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged; - - if (_parentFlyoutPage != null) - _parentFlyoutPage.PropertyChanged -= MultiPagePropertyChanged; - } - - protected virtual void OnElementChanged(VisualElementChangedEventArgs e) - { - EventHandler changed = ElementChanged; - if (changed != null) - changed(this, e); - } - - WBrush GetBarBackgroundColorBrush() - { - object defaultColor = GetDefaultColor(); - - if (VirtualView.BarBackgroundColor.IsDefault() && defaultColor != null) - return (WBrush)defaultColor; - - return Maui.ColorExtensions.ToNative(VirtualView.BarBackgroundColor); - } - - static WBrush? GetBarBackgroundBrush(NavigationPage navigationPage) - { - var barBackground = navigationPage.BarBackground; - object defaultColor = GetDefaultColor(); - - if (!Brush.IsNullOrEmpty(barBackground)) - return barBackground.ToBrush(); - - if (navigationPage.BarBackgroundColor != null) - return navigationPage.BarBackgroundColor.ToNative(); - - if (defaultColor != null) - return (WBrush)defaultColor; - - return null; - } - - static WBrush GetBarForegroundBrush(NavigationPage navigationPage) - { - object defaultColor = Microsoft.UI.Xaml.Application.Current.Resources["ApplicationForegroundThemeBrush"]; - if (navigationPage.BarTextColor.IsDefault()) - return (WBrush)defaultColor; - return Maui.ColorExtensions.ToNative(navigationPage.BarTextColor); - } - - bool GetIsNavBarPossible() - { - return _showTitle; - } - - void LookupRelevantParents() - { - var parentPages = VirtualView.GetParentPages(); - - if (_parentTabbedPage != null) - _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged; - - if (_parentFlyoutPage != null) - _parentFlyoutPage.PropertyChanged -= MultiPagePropertyChanged; - - foreach (Page parentPage in parentPages) - { - _parentTabbedPage = parentPage as TabbedPage; - _parentFlyoutPage = parentPage as FlyoutPage; - } - - if (_parentTabbedPage != null) - _parentTabbedPage.PropertyChanged += MultiPagePropertyChanged; - if (_parentFlyoutPage != null) - _parentFlyoutPage.PropertyChanged += MultiPagePropertyChanged; - - UpdateShowTitle(); - UpdateTitleOnParents(); - _parentsLookedUp = true; - } - - void MultiPagePropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "CurrentPage" || e.PropertyName == "Detail") - { - UpdateTitleOnParents(); - UpdateTitleIcon(); - UpdateTitleView(); - } - } - - void OnBackClicked(object sender, RoutedEventArgs e) - { - VirtualView?.SendBackButtonPressed(); - } - - void OnChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - UpdateBackButton(); - } - - // TODO MAUI: hmmmmmm can we make this not be property changed based? - void OnCurrentPagePropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName) - UpdateBackButton(); - else if (e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName) - UpdateBackButtonTitle(); - else if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName) - UpdateTitleVisible(); - else if (e.PropertyName == Page.TitleProperty.PropertyName) - UpdateTitleOnParents(); - else if (e.PropertyName == NavigationPage.TitleIconImageSourceProperty.PropertyName) - UpdateTitleIcon(); - else if (e.PropertyName == NavigationPage.TitleViewProperty.PropertyName) - UpdateTitleView(); - } - - void OnElementAppearing(object? sender, EventArgs e) - { - UpdateTitleVisible(); - UpdateBackButton(); - } - - void OnLoaded(object? sender, RoutedEventArgs args) - { - if (VirtualView == null) - return; - - VirtualView.SendAppearing(); - UpdateBackButton(); - UpdateTitleOnParents(); - - if (_parentFlyoutPage != null) - { - UpdateTitleView(); - UpdateTitleIcon(); - } - } - - void OnNativeSizeChanged(object? sender, SizeChangedEventArgs e) - { - UpdateContainerArea(); - } - - void OnPointerPressed(object? sender, PointerRoutedEventArgs e) - { - if (e.Handled) - return; - - var point = e.GetCurrentPoint(NativeView); - if (point == null) - return; - - if (point.PointerDeviceType != PointerDeviceType.Mouse) - return; - - if (point.Properties.IsXButton1Pressed) - { - e.Handled = true; - OnBackClicked(NativeView, e); - } - } - - protected virtual void OnPopRequested(object? sender, NavigationRequestedEventArgs e) - { - var newCurrent = VirtualView.Peek(1); - SetPage(newCurrent, e.Animated, true); - } - - protected virtual void OnPopToRootRequested(object? sender, NavigationRequestedEventArgs e) - { - SetPage(e.Page, e.Animated, true); - } - - protected virtual void OnPushRequested(object? sender, NavigationRequestedEventArgs e) - { - SetPage(e.Page, e.Animated, false); - } - - void OnUnloaded(object? sender, RoutedEventArgs args) - { - VirtualView?.SendDisappearing(); - } - - void PushExistingNavigationStack() - { - foreach (var page in VirtualView.Pages) - { - SetPage(page, false, false); - } - } - - void SetPage(Page? page, bool isAnimated, bool isPopping) - { - if (_currentPage != null) - { - if (isPopping) - { - _currentPage.Cleanup(); - NativeView.TitleView?.Cleanup(); - } - - NativeView.Content = null; - _currentPage.PropertyChanged -= OnCurrentPagePropertyChanged; - } - - if (!isPopping) - _previousPage = _currentPage; - - _currentPage = page; - - if (page == null) - return; - - UpdateBackButton(); - UpdateBackButtonTitle(); - - page.PropertyChanged += OnCurrentPagePropertyChanged; - - IViewHandler renderer = page.GetOrCreateHandler(this.MauiContext); - - UpdateTitleVisible(); - UpdateTitleOnParents(); - UpdateTitleView(); - - SetupPageTransition(_transition, isAnimated, isPopping); - - NativeView.Content = renderer.NativeView; - NativeView.DataContext = page; - } - - protected virtual void SetupPageTransition(Transition transition, bool isAnimated, bool isPopping) - { - PageControl nativeView = NativeView; - if (isAnimated && transition == null) - { - transition = new EntranceThemeTransition(); - _transition = (EntranceThemeTransition)transition; - nativeView.ContentTransitions = new TransitionCollection(); - } - - if (!isAnimated && nativeView.ContentTransitions?.Count > 0) - { - nativeView.ContentTransitions.Clear(); - } - else if (isAnimated && - nativeView.ContentTransitions != null && - nativeView.ContentTransitions.Contains(transition) == false) - { - nativeView.ContentTransitions.Clear(); - nativeView.ContentTransitions.Add(transition); - } - } - - void UpdateBackButtonTitle() - { - string title; - if (_previousPage != null) - title = NavigationPage.GetBackButtonTitle(_previousPage); - else - title = String.Empty; - - NativeView.BackButtonTitle = title; - } - - void UpdateContainerArea() - { - VirtualView.ContainerArea = new Rectangle(0, 0, NativeView.ContentWidth, NativeView.ContentHeight); - } - - void UpdateTitleVisible() - { - UpdateTitleOnParents(); - - bool showing = NativeView.TitleVisibility == Visibility.Visible; - bool newValue = GetIsNavBarPossible() && NavigationPage.GetHasNavigationBar(_currentPage); - if (showing == newValue) - return; - - NativeView.TitleVisibility = newValue ? Visibility.Visible : Visibility.Collapsed; - - // Force ContentHeight/Width to update, doesn't work from inside PageControl for some reason - NativeView.UpdateLayout(); - UpdateContainerArea(); - } - - void UpdateShowTitle() - { - ((ITitleProvider)this).ShowTitle = _parentTabbedPage == null && _parentFlyoutPage == null; - } - - static object GetDefaultColor() - { - return Microsoft.UI.Xaml.Application.Current.Resources["SystemControlBackgroundChromeMediumLowBrush"]; - } - - void UpdateBackButton() - { - if (_currentPage == null) - { - return; - } - - bool showBackButton = VirtualView.InternalChildren.Count > 1 && NavigationPage.GetHasBackButton(_currentPage); - if (NativeVersion.IsDesktop) - { - //TODO MAUI: this means it's running as a desktop app - } - else - { - var navManager = SystemNavigationManager.GetForCurrentView(); - if(navManager != null) - navManager.AppViewBackButtonVisibility = showBackButton ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed; - } - - NativeView.SetBackButtonTitle(VirtualView); - } - - void UpdateTitleOnParents() - { - if (VirtualView == null || _currentPage == null) - return; - - ITitleProvider? render = null; - if (_parentTabbedPage != null) - { - render = _parentTabbedPage.Handler as ITitleProvider; - if (render != null) - render.ShowTitle = (_parentTabbedPage.CurrentPage == VirtualView) && NavigationPage.GetHasNavigationBar(_currentPage); - } - - if (_parentFlyoutPage != null) - { - render = _parentFlyoutPage.Handler as ITitleProvider; - if (render != null) - render.ShowTitle = (_parentFlyoutPage.Detail == VirtualView) && NavigationPage.GetHasNavigationBar(_currentPage); - } - - if (render != null && render.ShowTitle) - { - render.Title = _currentPage.Title; - - if (!Brush.IsNullOrEmpty(VirtualView.BarBackground)) - render.BarBackgroundBrush = GetBarBackgroundBrush(VirtualView); - else - render.BarBackgroundBrush = GetBarBackgroundColorBrush(); - - render.BarForegroundBrush = GetBarForegroundBrush(VirtualView); - } - - if (_showTitle || (render != null && render.ShowTitle)) - { - ToolbarManager.UpdateToolbarItems(VirtualView) - .FireAndForget((e)=> Log.Warning(nameof(NavigationPage), $"{e}")); - } - } - - void UpdatePadding() - { - NativeView.TitleInset = VirtualView.Padding.Left; - } - - void UpdateTitleColor() - { - (this as ITitleProvider).BarForegroundBrush = GetBarForegroundBrush(VirtualView); - } - - void UpdateNavigationBarBackground() - { - (this as ITitleProvider).BarBackgroundBrush = GetBarBackgroundBrush(VirtualView); - } - - void UpdateTitleIcon() => - UpdateTitleIconAsync() - .FireAndForget(errorCallback: (e) => Log.Warning(nameof(TitleIcon), $"{e}")); - - async Task UpdateTitleIconAsync() - { - var page = _currentPage; - if (page == null) - return; - - ImageSource source = NavigationPage.GetTitleIconImageSource(page); - - TitleIcon = await source.ToWindowsImageSourceAsync(); - - if (NativeView == null || _currentPage != page) - return; - - NativeView.TitleIcon = TitleIcon; - - if (_parentFlyoutPage != null && this is ITitleIconProvider parent) - parent.TitleIcon = TitleIcon; - - NativeView.UpdateLayout(); - UpdateContainerArea(); - } - - void UpdateTitleView() - { - // if the life cycle hasn't reached the point where _parentFlyoutPage gets wired up then - // don't update the title view - if (_currentPage == null || !_parentsLookedUp) - return; - - // If the container TitleView gets initialized before the FP TitleView it causes the - // FP TitleView to not render correctly - if (_parentFlyoutPage != null) - { - if (this is ITitleViewProvider parent) - parent.TitleView = TitleView; - } - else if (_parentFlyoutPage == null) - NativeView.TitleView = TitleView; - - } - - void UpdateToolbarPlacement() - { - if (NativeView == null) - { - return; - } - - NativeView.ToolbarPlacement = VirtualView.OnThisPlatform().GetToolbarPlacement(); - } - - void UpdateToolbarDynamicOverflowEnabled() - { - if (NativeView == null) - { - return; - } - - NativeView.ToolbarDynamicOverflowEnabled = VirtualView.OnThisPlatform().GetToolbarDynamicOverflowEnabled(); - } - - public static void MapPadding(NavigationPageHandler handler, NavigationPage view) => - handler.UpdatePadding(); - - public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage view) => handler.UpdateTitleColor(); - - public static void MapBarBackground(NavigationPageHandler handler, NavigationPage view) => handler.UpdateNavigationBarBackground(); - - // TODO MAUI: Task Based Mappers? - public static void MapTitleIcon(NavigationPageHandler handler, NavigationPage view) => handler.UpdateTitleIcon(); - - public static void MapTitleView(NavigationPageHandler handler, NavigationPage view) => handler.UpdateTitleView(); - - public static void MapToolbarPlacement(NavigationPageHandler handler, NavigationPage view) => handler.UpdateToolbarPlacement(); - - public static void MapToolbarDynamicOverflowEnabled(NavigationPageHandler handler, NavigationPage view) => handler.UpdateToolbarDynamicOverflowEnabled(); - } -} diff --git a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.cs b/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.cs deleted file mode 100644 index 952591e22c8e..000000000000 --- a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.Maui.Handlers; - -#if WINDOWS -using static Microsoft.Maui.Controls.PlatformConfiguration.WindowsSpecific.Page; -#endif - -namespace Microsoft.Maui.Controls.Handlers -{ - public partial class NavigationPageHandler - { - public static PropertyMapper NavigationPageMapper - = new PropertyMapper(ViewHandler.ViewMapper) - { - [NavigationPage.BarTextColorProperty.PropertyName] = MapBarTextColor, - [NavigationPage.BarBackgroundColorProperty.PropertyName] = MapBarBackground, - [NavigationPage.BarBackgroundProperty.PropertyName] = MapBarBackground, - [nameof(IPadding.Padding)] = MapPadding, - [nameof(NavigationPage.TitleIconImageSourceProperty.PropertyName)] = MapTitleIcon, - [nameof(NavigationPage.TitleViewProperty.PropertyName)] = MapTitleView, - -#if WINDOWS - [nameof(ToolbarPlacementProperty.PropertyName)] = MapToolbarPlacement, - [nameof(ToolbarDynamicOverflowEnabledProperty.PropertyName)] = MapToolbarDynamicOverflowEnabled, -#endif - }; - - public NavigationPageHandler() : base(NavigationPageMapper) - { - } - } -} diff --git a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.iOS.cs b/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.iOS.cs deleted file mode 100644 index 65ec233621b7..000000000000 --- a/src/Controls/src/Core/Handlers/NavigationPage/NavigationPageHandler.iOS.cs +++ /dev/null @@ -1,202 +0,0 @@ -#nullable enable - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Maui.Controls.Internals; -using Microsoft.Maui.Controls.Platform; -using Microsoft.Maui.Handlers; -using UIKit; - -namespace Microsoft.Maui.Controls.Handlers -{ - public partial class NavigationPageHandler : - ViewHandler, INativeViewHandler - { - ControlsNavigationController _controlsNavigationController; - UIViewController? INativeViewHandler.ViewController => _controlsNavigationController; - - protected override UIView CreateNativeView() - { - _controlsNavigationController = new ControlsNavigationController(this); - - if (_controlsNavigationController.View == null) - throw new NullReferenceException("ControlsNavigationController.View is null"); - - return _controlsNavigationController.View; - } - - public static void MapPadding(NavigationPageHandler handler, NavigationPage view) { } - - public static void MapTitleIcon(NavigationPageHandler handler, NavigationPage view) { } - - public static void MapTitleView(NavigationPageHandler handler, NavigationPage view) { } - - public static void MapBarBackground(NavigationPageHandler handler, NavigationPage view) - { - var NavPage = handler.VirtualView; - var barBackgroundBrush = NavPage.BarBackground; - - if (Brush.IsNullOrEmpty(barBackgroundBrush) && - NavPage.BarBackgroundColor != null) - barBackgroundBrush = new SolidColorBrush(NavPage.BarBackgroundColor); - - if (barBackgroundBrush == null) - return; - - var navController = handler._controlsNavigationController; - var NavigationBar = navController.NavigationBar; - - if (NativeVersion.IsAtLeast(13)) - { - var navigationBarAppearance = NavigationBar.StandardAppearance; - - navigationBarAppearance.ConfigureWithOpaqueBackground(); - - //if (barBackgroundColor == null) - //{ - // navigationBarAppearance.BackgroundColor = ColorExtensions.BackgroundColor; - - // var parentingViewController = GetParentingViewController(); - // parentingViewController?.SetupDefaultNavigationBarAppearance(); - //} - //else - // navigationBarAppearance.BackgroundColor = barBackgroundColor.ToUIColor(); - - var backgroundImage = NavigationBar.GetBackgroundImage(barBackgroundBrush); - navigationBarAppearance.BackgroundImage = backgroundImage; - - NavigationBar.CompactAppearance = navigationBarAppearance; - NavigationBar.StandardAppearance = navigationBarAppearance; - NavigationBar.ScrollEdgeAppearance = navigationBarAppearance; - } - else - { - var backgroundImage = NavigationBar.GetBackgroundImage(barBackgroundBrush); - NavigationBar.SetBackgroundImage(backgroundImage, UIBarMetrics.Default); - } - } - - public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage view) - { - var NavPage = handler.VirtualView; - - var navController = handler._controlsNavigationController; - var NavigationBar = navController.NavigationBar; - - var barTextColor = NavPage.BarTextColor; - if (NavigationBar == null) - return; - - // Determine new title text attributes via global static data - var globalTitleTextAttributes = UINavigationBar.Appearance.TitleTextAttributes; - var titleTextAttributes = new UIStringAttributes - { - ForegroundColor = barTextColor == null ? globalTitleTextAttributes?.ForegroundColor : barTextColor.ToNative(), - Font = globalTitleTextAttributes?.Font - }; - - // Determine new large title text attributes via global static data - var largeTitleTextAttributes = titleTextAttributes; - if (NativeVersion.IsAtLeast(11)) - { - var globalLargeTitleTextAttributes = UINavigationBar.Appearance.LargeTitleTextAttributes; - - largeTitleTextAttributes = new UIStringAttributes - { - ForegroundColor = barTextColor == null ? globalLargeTitleTextAttributes?.ForegroundColor : barTextColor.ToNative(), - Font = globalLargeTitleTextAttributes?.Font - }; - } - - if (NativeVersion.IsAtLeast(13)) - { - if (NavigationBar.CompactAppearance != null) - { - NavigationBar.CompactAppearance.TitleTextAttributes = titleTextAttributes; - NavigationBar.CompactAppearance.LargeTitleTextAttributes = largeTitleTextAttributes; - } - - NavigationBar.StandardAppearance.TitleTextAttributes = titleTextAttributes; - NavigationBar.StandardAppearance.LargeTitleTextAttributes = largeTitleTextAttributes; - - if (NavigationBar.ScrollEdgeAppearance != null) - { - NavigationBar.ScrollEdgeAppearance.TitleTextAttributes = titleTextAttributes; - NavigationBar.ScrollEdgeAppearance.LargeTitleTextAttributes = largeTitleTextAttributes; - } - } - else - { - NavigationBar.TitleTextAttributes = titleTextAttributes; - - if (NativeVersion.IsAtLeast(11)) - NavigationBar.LargeTitleTextAttributes = largeTitleTextAttributes; - } - - //// set Tint color (i. e. Back Button arrow and Text) - //var iconColor = Current != null ? NavigationPage.GetIconColor(Current) : null; - //if (iconColor == null) - // iconColor = barTextColor; - - //NavigationBar.TintColor = iconColor == null || NavPage.OnThisPlatform().GetStatusBarTextColorMode() == StatusBarTextColorMode.DoNotAdjust - // ? UINavigationBar.Appearance.TintColor - // : iconColor.ToUIColor(); - } - - - protected override void ConnectHandler(UIView nativeView) - { - base.ConnectHandler(nativeView); - - if (VirtualView == null) - return; - - VirtualView.PushRequested += OnPushRequested; - VirtualView.PopRequested += OnPopRequested; - _controlsNavigationController.LoadPages(this.MauiContext); - - //VirtualView.PopToRootRequested += OnPopToRootRequested; - //VirtualView.RemovePageRequested += OnRemovedPageRequested; - //VirtualView.InsertPageBeforeRequested += OnInsertPageBeforeRequested; - } - - protected override void DisconnectHandler(UIView nativeView) - { - base.DisconnectHandler(nativeView); - - if (VirtualView == null) - return; - - VirtualView.PushRequested -= OnPushRequested; - VirtualView.PopRequested -= OnPopRequested; - //VirtualView.PopToRootRequested -= OnPopToRootRequested; - //VirtualView.RemovePageRequested -= OnRemovedPageRequested; - //VirtualView.InsertPageBeforeRequested -= OnInsertPageBeforeRequested; - } - - void OnPushRequested(object? sender, NavigationRequestedEventArgs e) - { - _controlsNavigationController? - .OnPushRequested(e, this.MauiContext); - } - - void OnPopRequested(object? sender, NavigationRequestedEventArgs e) - { - _controlsNavigationController? - .OnPopRequestedAsync(e) - .FireAndForget((exc) => Log.Warning(nameof(NavigationPage), $"{exc}")); - } - - internal void SendPopping(Task popTask) - { - if (VirtualView == null) - return; - - VirtualView.PopAsyncInner(false, true, true) - .FireAndForget((exc) => Log.Warning(nameof(NavigationPage), $"{exc}")); - } - } -} diff --git a/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs b/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs index 83d89da5876c..f514b82aac38 100644 --- a/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs +++ b/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs @@ -42,6 +42,7 @@ public static partial class AppHostBuilderExtensions { typeof(Shapes.Rectangle), typeof(ShapeViewHandler) }, { typeof(Layout), typeof(LayoutHandler) }, { typeof(Window), typeof(WindowHandler) }, + //{ typeof(NavigationPage), typeof(NavigationPageHandler) }, }; public static IMauiHandlersCollection AddMauiControlsHandlers(this IMauiHandlersCollection handlersCollection) diff --git a/src/Controls/src/Core/NavigationPage.cs b/src/Controls/src/Core/NavigationPage.cs index 8eaf24b59b03..09813c7a4ea4 100644 --- a/src/Controls/src/Core/NavigationPage.cs +++ b/src/Controls/src/Core/NavigationPage.cs @@ -37,11 +37,13 @@ public partial class NavigationPage : Page, IPageContainer, IBarElement, I INavigationPageController NavigationPageController => this; + partial void Init(); public NavigationPage() { _platformConfigurationRegistry = new Lazy>(() => new PlatformConfigurationRegistry(this)); Navigation = new NavigationImpl(this); + Init(); } public NavigationPage(Page root) : this() diff --git a/src/Controls/src/Core/OrderedDictionary.cs b/src/Controls/src/Core/OrderedDictionary.cs index 84c40e0af238..8b2567bcac3b 100644 --- a/src/Controls/src/Core/OrderedDictionary.cs +++ b/src/Controls/src/Core/OrderedDictionary.cs @@ -32,6 +32,7 @@ using System.Collections.ObjectModel; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui; namespace Cadenza.Collections { diff --git a/src/Core/src/Core/INavigationView.cs b/src/Core/src/Core/INavigationView.cs new file mode 100644 index 000000000000..095cab297c8c --- /dev/null +++ b/src/Core/src/Core/INavigationView.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Maui +{ + internal interface INavigationView : IView + { + IReadOnlyList ModalStack { get; } + + IReadOnlyList NavigationStack { get; } + + void InsertPageBefore(IView page, IView before); + Task PopAsync(); + Task PopAsync(bool animated); + Task PopModalAsync(); + Task PopModalAsync(bool animated); + Task PopToRootAsync(); + Task PopToRootAsync(bool animated); + + Task PushAsync(IView page); + + Task PushAsync(IView page, bool animated); + Task PushModalAsync(IView page); + Task PushModalAsync(IView page, bool animated); + + void RemovePage(IView page); + } +} diff --git a/src/Controls/src/Core/ArrayExtensions.cs b/src/Core/src/Extensions/ArrayExtensions.cs similarity index 90% rename from src/Controls/src/Core/ArrayExtensions.cs rename to src/Core/src/Extensions/ArrayExtensions.cs index 46371125b11a..c9f71ee671cb 100644 --- a/src/Controls/src/Core/ArrayExtensions.cs +++ b/src/Core/src/Extensions/ArrayExtensions.cs @@ -1,7 +1,6 @@ using System; -using Microsoft.Maui.Controls.Internals; -namespace Microsoft.Maui.Controls.Platform +namespace Microsoft.Maui { internal static class ArrayExtensions { diff --git a/src/Core/src/Extensions/EnumerableExtensions.cs b/src/Core/src/Extensions/EnumerableExtensions.cs new file mode 100644 index 000000000000..4c60c9e64d92 --- /dev/null +++ b/src/Core/src/Extensions/EnumerableExtensions.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Maui +{ + internal static class EnumerableExtensions + { + public static void ForEach(this IEnumerable enumeration, Action action) + { + foreach (T item in enumeration) + { + action(item); + } + } + + public static IDictionary> GroupToDictionary(this IEnumerable enumeration, Func func) + where TKey : notnull + { + var result = new Dictionary>(); + foreach (TSource item in enumeration) + { + var group = func(item); + if (!result.ContainsKey(group)) + result.Add(group, new List { item }); + else + result[group].Add(item); + } + return result; + } + + public static int IndexOf(this IEnumerable enumerable, T item) + { + if (enumerable == null) + throw new ArgumentNullException("enumerable"); + + var i = 0; + foreach (T element in enumerable) + { + if (Equals(element, item)) + return i; + + i++; + } + + return -1; + } + + public static int IndexOf(this IEnumerable enumerable, Func predicate) + { + var i = 0; + foreach (T element in enumerable) + { + if (predicate(element)) + return i; + + i++; + } + + return -1; + } + + public static T Last(this IList self) => self[self.Count - 1]; + } +} diff --git a/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Android.cs b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Android.cs new file mode 100644 index 000000000000..4d5f9f158b44 --- /dev/null +++ b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Android.cs @@ -0,0 +1,268 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using Android.Runtime; +using Android.Views; +using AndroidX.AppCompat.Widget; +using AndroidX.Navigation; +using AndroidX.Navigation.Fragment; +using Google.Android.Material.AppBar; +using AView = Android.Views.View; + +namespace Microsoft.Maui.Handlers +{ + internal partial class NavigationPageHandler : + ViewHandler + { + private NavHostFragment? _navHost; + private FragmentNavigator? _fragmentNavigator; + private Toolbar? _toolbar; + private AppBarLayout? _appBar; + + NavHostFragment NavHost + { + get => _navHost ?? throw new InvalidOperationException($"NavHost cannot be null"); + set => _navHost = value; + } + + FragmentNavigator FragmentNavigator + { + get => _fragmentNavigator ?? throw new InvalidOperationException($"FragmentNavigator cannot be null"); + set => _fragmentNavigator = value; + } + + int NativeNavigationStackCount => NavHost?.NavController.BackStack.Size() - 1 ?? 0; + int NavigationStackCount => VirtualView?.NavigationStack.Count ?? 0; + + internal Toolbar Toolbar + { + get => _toolbar ?? throw new InvalidOperationException($"ToolBar cannot be null"); + set => _toolbar = value; + } + + internal AppBarLayout AppBar + { + get => _appBar ?? throw new InvalidOperationException($"AppBar cannot be null"); + set => _appBar = value; + } + + protected override AView CreateNativeView() + { + LayoutInflater? li = LayoutInflater.From(Context); + _ = li ?? throw new InvalidOperationException($"LayoutInflater cannot be null"); + + var view = li.Inflate(Resource.Layout.navigationlayout, null).JavaCast(); + _ = view ?? throw new InvalidOperationException($"Resource.Layout.navigationlayout view not found"); + + _toolbar = view.FindViewById(Resource.Id.maui_toolbar); + _appBar = view.FindViewById(Resource.Id.appbar); + return view; + } + + protected override void ConnectHandler(AView nativeView) + { + var fragmentManager = Context.GetFragmentManager(); + _ = fragmentManager ?? throw new InvalidOperationException($"GetFragmentManager returned null"); + _ = VirtualView ?? throw new InvalidOperationException($"VirtualView cannot be null"); + + NavHost = (NavHostFragment) + fragmentManager.FindFragmentById(Resource.Id.nav_host); + + FragmentNavigator = + (FragmentNavigator)NavHost + .NavController + .NavigatorProvider + .GetNavigator(Java.Lang.Class.FromType(typeof(FragmentNavigator))); + + + var navGraphNavigator = + (NavGraphNavigator)NavHost + .NavController + .NavigatorProvider + .GetNavigator(Java.Lang.Class.FromType(typeof(NavGraphNavigator))); + + base.ConnectHandler(nativeView); + + var inflater = NavHost.NavController.NavInflater; + NavGraph graph = new NavGraph(navGraphNavigator); + + NavDestination navDestination; + List destinations = new List(); + foreach (var page in VirtualView.NavigationStack) + { + navDestination = + MauiFragmentNavDestination. + AddDestination( + page, + this, + graph, + FragmentNavigator); + + destinations.Add(navDestination.Id); + } + + graph.StartDestination = destinations[0]; + + NavHost.NavController.SetGraph(graph, null); + + for (var i = NativeNavigationStackCount; i < NavigationStackCount; i++) + { + var dest = destinations[i]; + NavHost.NavController.Navigate(dest); + } + } + + private static void PushAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + if (arg3 is not MauiNavigationRequestedEventArgs e) + return; + + var destination = + MauiFragmentNavDestination.AddDestination(e.Page, arg1, arg1.NavHost.NavController.Graph, arg1.FragmentNavigator); + + arg1.NavHost.NavController.Navigate(destination.Id, null); + } + + private static void PopAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + arg1.NavHost.NavController.NavigateUp(); + } + + internal void OnPop() + { + VirtualView + .PopAsync() + .FireAndForget((e) => + { + //Log.Warning(nameof(NavigationPageHandler), $"{e}"); + }); + } + + //void OnPopped(object? sender, NavigationRequestedEventArgs e) + //{ + // NavHost.NavController.NavigateUp(); + //} + + //void UpdatePadding() + //{ + //} + + //void UpdateBarTextColor() + //{ + // UpdateBarBackground(); + //} + + //void UpdateBarBackground() + //{ + // var context = Context; + // var bar = Toolbar; + // //ActionBarDrawerToggle toggle = _drawerToggle; + + // if (bar == null) + // return; + + // //bool isNavigated = NavigationPageController.StackDepth > 1; + // //bar.NavigationIcon = null; + // var navPage = VirtualView; + + // //if (isNavigated) + // //{ + // // if (NavigationPage.GetHasBackButton(currentPage) && !Context.IsDesignerContext()) + // // { + // // if (toggle != null) + // // { + // // toggle.DrawerIndicatorEnabled = false; + // // toggle.SyncState(); + // // } + + // // var activity = (AppCompatActivity)context.GetActivity(); + // // var icon = new DrawerArrowDrawable(activity.SupportActionBar.ThemedContext); + // // icon.Progress = 1; + // // bar.NavigationIcon = icon; + + // // var prevPage = Element.Peek(1); + // // var backButtonTitle = NavigationPage.GetBackButtonTitle(prevPage); + // // _defaultNavigationContentDescription = backButtonTitle != null + // // ? bar.SetNavigationContentDescription(prevPage, backButtonTitle) + // // : bar.SetNavigationContentDescription(prevPage, _defaultNavigationContentDescription); + // // } + // // else if (toggle != null && _flyoutPage != null) + // // { + // // toggle.DrawerIndicatorEnabled = _flyoutPage.ShouldShowToolbarButton(); + // // toggle.SyncState(); + // // } + // //} + // //else + // //{ + // // if (toggle != null && _flyoutPage != null) + // // { + // // toggle.DrawerIndicatorEnabled = _flyoutPage.ShouldShowToolbarButton(); + // // toggle.SyncState(); + // // } + // //} + + // var tintColor = navPage.BarBackgroundColor; + // Brush barBackground = navPage.BarBackground; + + // if (barBackground == null && tintColor != null) + // barBackground = new SolidColorBrush(tintColor); + + // if (barBackground != null) + // bar.UpdateBackground(barBackground); + // //else if (tintColor == null) + // // bar.BackgroundTintMode = null; + // //else + // //{ + // // bar.Background = null; + // // bar.BackgroundTintMode = PorterDuff.Mode.Src; + // // bar.BackgroundTintList = ColorStateList.ValueOf(tintColor.ToNative()); + // //} + + // var textColor = navPage.BarTextColor; + // if (textColor != null) + // bar.SetTitleTextColor(textColor.ToNative().ToArgb()); + + // //var navIconColor = NavigationPage.GetIconColor(currentPage); + // //if (navIconColor != null && bar.NavigationIcon != null) + // // DrawableExtensions.SetColorFilter(bar.NavigationIcon, navIconColor, FilterMode.SrcAtop); + + // bar.Title = navPage.CurrentPage?.Title ?? string.Empty; + + // //if (_toolbar.NavigationIcon != null && textColor != null) + // //{ + // // var icon = _toolbar.NavigationIcon as DrawerArrowDrawable; + // // if (icon != null) + // // icon.Color = textColor.ToAndroid().ToArgb(); + // //} + + // //UpdateTitleIcon(); + + // //UpdateTitleView(); + //} + + //void UpdateTitleIcon() + //{ + //} + //void UpdateTitleView() + //{ + //} + + + //public static void MapPadding(NavigationPageHandler handler, INavigationView view) + // => handler.UpdatePadding(); + + //public static void MapBarTextColor(NavigationPageHandler handler, INavigationView view) + // => handler.UpdateBarTextColor(); + + //public static void MapBarBackground(NavigationPageHandler handler, INavigationView view) + // => handler.UpdateBarBackground(); + + //// TODO MAUI: Task Based Mappers? + //public static void MapTitleIcon(NavigationPageHandler handler, INavigationView view) + // => handler.UpdateTitleIcon(); + + //public static void MapTitleView(NavigationPageHandler handler, INavigationView view) + // => handler.UpdateTitleView(); + } +} diff --git a/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Standard.cs b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Standard.cs new file mode 100644 index 000000000000..d5743c136303 --- /dev/null +++ b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Standard.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Maui.Handlers; + +namespace Microsoft.Maui.Handlers +{ + internal partial class NavigationPageHandler : + ViewHandler + { + protected override object CreateNativeView() + { + throw new NotImplementedException(); + } + + private static void PushAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + throw new NotImplementedException(); + } + + private static void PopAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + throw new NotImplementedException(); + } + + //public static void MapPadding(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapBarTextColor(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapBarBackground(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapTitleIcon(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapTitleView(NavigationPageHandler handler, INavigationView view) { } + } +} diff --git a/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Windows.cs b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Windows.cs new file mode 100644 index 000000000000..6edc3424263d --- /dev/null +++ b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.Windows.cs @@ -0,0 +1,743 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Maui.Handlers; +using Microsoft.UI.Xaml; + +namespace Microsoft.Maui.Handlers +{ + internal partial class NavigationPageHandler : + ViewHandler + { + protected override FrameworkElement CreateNativeView() + { + throw new NotImplementedException(); + } + + private static void PushAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + throw new NotImplementedException(); + } + + private static void PopAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + throw new NotImplementedException(); + } + + //public static void MapPadding(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapBarTextColor(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapBarBackground(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapTitleIcon(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapTitleView(NavigationPageHandler handler, INavigationView view) { } + } +} + + + +//#nullable enable + +//using System; +//using System.Collections.Generic; +//using System.Collections.Specialized; +//using System.ComponentModel; +//using System.Threading.Tasks; +//using Windows.Devices.Input; +//using Windows.UI.Input; +//using Microsoft.UI.Xaml; +//using Microsoft.UI.Xaml.Controls; +//using Microsoft.UI.Xaml.Input; +//using Microsoft.UI.Xaml.Media.Animation; +//using Windows.UI.Core; +//using Microsoft.UI.Xaml.Data; +//using Microsoft.Maui.Controls.Internals; +//using static Microsoft.Maui.Controls.PlatformConfiguration.WindowsSpecific.Page; +//using WBrush = Microsoft.UI.Xaml.Media.Brush; +//using WImageSource = Microsoft.UI.Xaml.Media.ImageSource; +//using Microsoft.Maui.Graphics; +//using Microsoft.Maui.Handlers; +//using Microsoft.Maui.Controls.Platform; + +//namespace Microsoft.Maui.Controls.Handlers +//{ +// public partial class NavigationPageHandler : +// ViewHandler, ITitleProvider, ITitleIconProvider, +// ITitleViewProvider, IToolbarProvider, IToolBarForegroundBinder, IViewHandler +// { +// Page? _currentPage; +// Page? _previousPage; + +// FlyoutPage? _parentFlyoutPage; +// TabbedPage? _parentTabbedPage; +// bool _showTitle = true; +// VisualElementTracker _tracker; +// EntranceThemeTransition _transition; +// bool _parentsLookedUp = false; + +// public void BindForegroundColor(AppBar appBar) +// { +// SetAppBarForegroundBinding(appBar); +// } + +// public void BindForegroundColor(AppBarButton button) +// { +// SetAppBarForegroundBinding(button); +// } + +// protected VisualElementTracker Tracker +// { +// get { return _tracker; } +// set +// { +// if (_tracker == value) +// return; + +// if (_tracker != null) +// _tracker.Dispose(); + +// _tracker = value; +// } +// } + +// //public void Dispose() +// //{ +// // Dispose(true); +// //} + +// void SetAppBarForegroundBinding(FrameworkElement element) +// { +// element.SetBinding(Control.ForegroundProperty, +// new Microsoft.UI.Xaml.Data.Binding { Path = new PropertyPath("TitleBrush"), Source = NativeView, RelativeSource = new RelativeSource { Mode = RelativeSourceMode.TemplatedParent } }); +// } + +// WBrush ITitleProvider.BarBackgroundBrush +// { +// set +// { +// NativeView.ToolbarBackground = value; +// UpdateTitleOnParents(); +// } +// } + +// WBrush ITitleProvider.BarForegroundBrush +// { +// set +// { +// NativeView.TitleBrush = value; +// UpdateTitleOnParents(); +// } +// } + +// bool ITitleProvider.ShowTitle +// { +// get { return _showTitle; } +// set +// { +// if (_showTitle == value) +// return; + +// _showTitle = value; +// UpdateTitleVisible(); +// UpdateTitleOnParents(); +// } +// } + +// public string Title +// { +// get { return _currentPage?.Title ?? String.Empty; } + +// set { /*Not implemented but required by interface*/ } +// } + +// public WImageSource TitleIcon { get; set; } + +// public View? TitleView +// { +// get +// { +// if (_currentPage == null) +// return null; + +// return NavigationPage.GetTitleView(_currentPage) as View; +// } +// set { /*Not implemented but required by interface*/ } +// } + +// Task IToolbarProvider.GetCommandBarAsync() +// { +// return ((IToolbarProvider)NativeView)?.GetCommandBarAsync() ?? +// Task.FromResult((CommandBar?)null); +// } + +// public event EventHandler ElementChanged; + +// public override Size GetDesiredSize(double widthConstraint, double heightConstraint) +// { +// if (VirtualView == null) +// return Size.Zero; + +// var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint); +// IViewHandler childRenderer = VirtualView.CurrentPage.Handler; +// FrameworkElement? child = childRenderer.NativeView as FrameworkElement; +// if (child == null) +// return Size.Zero; + +// double oldWidth = child.Width; +// double oldHeight = child.Height; + +// child.Height = double.NaN; +// child.Width = double.NaN; + +// child.Measure(constraint); +// var result = new Size(Math.Ceiling(child.DesiredSize.Width), Math.Ceiling(child.DesiredSize.Height)); + +// child.Width = oldWidth; +// child.Height = oldHeight; + +// return result; +// } + +// protected override PageControl CreateNativeView() +// { +// return new PageControl(); +// } + +// public override void SetVirtualView(IView view) +// { +// base.SetVirtualView(view); + +// if (view != null && !(view is NavigationPage)) +// throw new ArgumentException("VirtualView must be a Page", nameof(view)); + +// if (view is NavigationPage np && np != null && np.CurrentPage is null) +// throw new InvalidOperationException( +// "NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage."); + +// } + +// protected override void ConnectHandler(PageControl nativeView) +// { +// base.ConnectHandler(nativeView); + +// var virtualView = VirtualView; + +// nativeView.PointerPressed += OnPointerPressed; +// nativeView.SizeChanged += OnNativeSizeChanged; +// Tracker = new BackgroundTracker(Control.BackgroundProperty) +// { +// Element = virtualView, +// Container = NativeView +// }; + +// SetPage(virtualView.CurrentPage, false, false); + +// nativeView.Loaded += OnLoaded; +// nativeView.Unloaded += OnUnloaded; + +// nativeView.DataContext = virtualView.CurrentPage; + +// // Move this somewhere else +// LookupRelevantParents(); + +// // Enforce consistency rules on toolbar (show toolbar if top-level page is Navigation Page) +// nativeView.ShouldShowToolbar = _parentFlyoutPage == null && _parentTabbedPage == null; +// if (_parentTabbedPage != null) +// virtualView.Appearing += OnElementAppearing; + +// virtualView.PushRequested += OnPushRequested; +// virtualView.PopRequested += OnPopRequested; +// virtualView.PopToRootRequested += OnPopToRootRequested; +// virtualView.InternalChildren.CollectionChanged += OnChildrenChanged; + +// if (!string.IsNullOrEmpty(virtualView.AutomationId)) +// nativeView.SetValue(Microsoft.UI.Xaml.Automation.AutomationProperties.AutomationIdProperty, virtualView.AutomationId); + +// PushExistingNavigationStack(); +// } + +// protected override void DisconnectHandler(PageControl nativeView) +// { +// base.DisconnectHandler(nativeView); + +// if (VirtualView == null) +// return; + +// VirtualView.PushRequested -= OnPushRequested; +// VirtualView.PopRequested -= OnPopRequested; +// VirtualView.PopToRootRequested -= OnPopToRootRequested; +// VirtualView.InternalChildren.CollectionChanged -= OnChildrenChanged; +// nativeView.PointerPressed -= OnPointerPressed; +// nativeView.SizeChanged -= OnNativeSizeChanged; +// nativeView.Loaded -= OnLoaded; +// nativeView.Unloaded -= OnUnloaded; + +// VirtualView.SendDisappearing(); + +// nativeView.PointerPressed -= OnPointerPressed; +// nativeView.SizeChanged -= OnNativeSizeChanged; +// nativeView.Loaded -= OnLoaded; +// nativeView.Unloaded -= OnUnloaded; + +// if (_parentTabbedPage != null) +// VirtualView.Appearing -= OnElementAppearing; + +// SetPage(null, false, true); +// _previousPage = null; + +// if (_parentTabbedPage != null) +// _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged; + +// if (_parentFlyoutPage != null) +// _parentFlyoutPage.PropertyChanged -= MultiPagePropertyChanged; +// } + +// protected virtual void OnElementChanged(VisualElementChangedEventArgs e) +// { +// EventHandler changed = ElementChanged; +// if (changed != null) +// changed(this, e); +// } + +// WBrush GetBarBackgroundColorBrush() +// { +// object defaultColor = GetDefaultColor(); + +// if (VirtualView.BarBackgroundColor.IsDefault() && defaultColor != null) +// return (WBrush)defaultColor; + +// return Maui.ColorExtensions.ToNative(VirtualView.BarBackgroundColor); +// } + +// static WBrush? GetBarBackgroundBrush(NavigationPage navigationPage) +// { +// var barBackground = navigationPage.BarBackground; +// object defaultColor = GetDefaultColor(); + +// if (!Brush.IsNullOrEmpty(barBackground)) +// return barBackground.ToBrush(); + +// if (navigationPage.BarBackgroundColor != null) +// return navigationPage.BarBackgroundColor.ToNative(); + +// if (defaultColor != null) +// return (WBrush)defaultColor; + +// return null; +// } + +// static WBrush GetBarForegroundBrush(NavigationPage navigationPage) +// { +// object defaultColor = Microsoft.UI.Xaml.Application.Current.Resources["ApplicationForegroundThemeBrush"]; +// if (navigationPage.BarTextColor.IsDefault()) +// return (WBrush)defaultColor; +// return Maui.ColorExtensions.ToNative(navigationPage.BarTextColor); +// } + +// bool GetIsNavBarPossible() +// { +// return _showTitle; +// } + +// void LookupRelevantParents() +// { +// var parentPages = VirtualView.GetParentPages(); + +// if (_parentTabbedPage != null) +// _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged; + +// if (_parentFlyoutPage != null) +// _parentFlyoutPage.PropertyChanged -= MultiPagePropertyChanged; + +// foreach (Page parentPage in parentPages) +// { +// _parentTabbedPage = parentPage as TabbedPage; +// _parentFlyoutPage = parentPage as FlyoutPage; +// } + +// if (_parentTabbedPage != null) +// _parentTabbedPage.PropertyChanged += MultiPagePropertyChanged; +// if (_parentFlyoutPage != null) +// _parentFlyoutPage.PropertyChanged += MultiPagePropertyChanged; + +// UpdateShowTitle(); +// UpdateTitleOnParents(); +// _parentsLookedUp = true; +// } + +// void MultiPagePropertyChanged(object? sender, PropertyChangedEventArgs e) +// { +// if (e.PropertyName == "CurrentPage" || e.PropertyName == "Detail") +// { +// UpdateTitleOnParents(); +// UpdateTitleIcon(); +// UpdateTitleView(); +// } +// } + +// void OnBackClicked(object sender, RoutedEventArgs e) +// { +// VirtualView?.SendBackButtonPressed(); +// } + +// void OnChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e) +// { +// UpdateBackButton(); +// } + +// // TODO MAUI: hmmmmmm can we make this not be property changed based? +// void OnCurrentPagePropertyChanged(object? sender, PropertyChangedEventArgs e) +// { +// if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName) +// UpdateBackButton(); +// else if (e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName) +// UpdateBackButtonTitle(); +// else if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName) +// UpdateTitleVisible(); +// else if (e.PropertyName == Page.TitleProperty.PropertyName) +// UpdateTitleOnParents(); +// else if (e.PropertyName == NavigationPage.TitleIconImageSourceProperty.PropertyName) +// UpdateTitleIcon(); +// else if (e.PropertyName == NavigationPage.TitleViewProperty.PropertyName) +// UpdateTitleView(); +// } + +// void OnElementAppearing(object? sender, EventArgs e) +// { +// UpdateTitleVisible(); +// UpdateBackButton(); +// } + +// void OnLoaded(object? sender, RoutedEventArgs args) +// { +// if (VirtualView == null) +// return; + +// VirtualView.SendAppearing(); +// UpdateBackButton(); +// UpdateTitleOnParents(); + +// if (_parentFlyoutPage != null) +// { +// UpdateTitleView(); +// UpdateTitleIcon(); +// } +// } + +// void OnNativeSizeChanged(object? sender, SizeChangedEventArgs e) +// { +// UpdateContainerArea(); +// } + +// void OnPointerPressed(object? sender, PointerRoutedEventArgs e) +// { +// if (e.Handled) +// return; + +// var point = e.GetCurrentPoint(NativeView); +// if (point == null) +// return; + +// if (point.PointerDeviceType != PointerDeviceType.Mouse) +// return; + +// if (point.Properties.IsXButton1Pressed) +// { +// e.Handled = true; +// OnBackClicked(NativeView, e); +// } +// } + +// protected virtual void OnPopRequested(object? sender, NavigationRequestedEventArgs e) +// { +// var newCurrent = VirtualView.Peek(1); +// SetPage(newCurrent, e.Animated, true); +// } + +// protected virtual void OnPopToRootRequested(object? sender, NavigationRequestedEventArgs e) +// { +// SetPage(e.Page, e.Animated, true); +// } + +// protected virtual void OnPushRequested(object? sender, NavigationRequestedEventArgs e) +// { +// SetPage(e.Page, e.Animated, false); +// } + +// void OnUnloaded(object? sender, RoutedEventArgs args) +// { +// VirtualView?.SendDisappearing(); +// } + +// void PushExistingNavigationStack() +// { +// foreach (var page in VirtualView.Pages) +// { +// SetPage(page, false, false); +// } +// } + +// void SetPage(Page? page, bool isAnimated, bool isPopping) +// { +// if (_currentPage != null) +// { +// if (isPopping) +// { +// _currentPage.Cleanup(); +// NativeView.TitleView?.Cleanup(); +// } + +// NativeView.Content = null; +// _currentPage.PropertyChanged -= OnCurrentPagePropertyChanged; +// } + +// if (!isPopping) +// _previousPage = _currentPage; + +// _currentPage = page; + +// if (page == null) +// return; + +// UpdateBackButton(); +// UpdateBackButtonTitle(); + +// page.PropertyChanged += OnCurrentPagePropertyChanged; + +// IViewHandler renderer = page.GetOrCreateHandler(this.MauiContext); + +// UpdateTitleVisible(); +// UpdateTitleOnParents(); +// UpdateTitleView(); + +// SetupPageTransition(_transition, isAnimated, isPopping); + +// NativeView.Content = renderer.NativeView; +// NativeView.DataContext = page; +// } + +// protected virtual void SetupPageTransition(Transition transition, bool isAnimated, bool isPopping) +// { +// PageControl nativeView = NativeView; +// if (isAnimated && transition == null) +// { +// transition = new EntranceThemeTransition(); +// _transition = (EntranceThemeTransition)transition; +// nativeView.ContentTransitions = new TransitionCollection(); +// } + +// if (!isAnimated && nativeView.ContentTransitions?.Count > 0) +// { +// nativeView.ContentTransitions.Clear(); +// } +// else if (isAnimated && +// nativeView.ContentTransitions != null && +// nativeView.ContentTransitions.Contains(transition) == false) +// { +// nativeView.ContentTransitions.Clear(); +// nativeView.ContentTransitions.Add(transition); +// } +// } + +// void UpdateBackButtonTitle() +// { +// string title; +// if (_previousPage != null) +// title = NavigationPage.GetBackButtonTitle(_previousPage); +// else +// title = String.Empty; + +// NativeView.BackButtonTitle = title; +// } + +// void UpdateContainerArea() +// { +// VirtualView.ContainerArea = new Rectangle(0, 0, NativeView.ContentWidth, NativeView.ContentHeight); +// } + +// void UpdateTitleVisible() +// { +// UpdateTitleOnParents(); + +// bool showing = NativeView.TitleVisibility == Visibility.Visible; +// bool newValue = GetIsNavBarPossible() && NavigationPage.GetHasNavigationBar(_currentPage); +// if (showing == newValue) +// return; + +// NativeView.TitleVisibility = newValue ? Visibility.Visible : Visibility.Collapsed; + +// // Force ContentHeight/Width to update, doesn't work from inside PageControl for some reason +// NativeView.UpdateLayout(); +// UpdateContainerArea(); +// } + +// void UpdateShowTitle() +// { +// ((ITitleProvider)this).ShowTitle = _parentTabbedPage == null && _parentFlyoutPage == null; +// } + +// static object GetDefaultColor() +// { +// return Microsoft.UI.Xaml.Application.Current.Resources["SystemControlBackgroundChromeMediumLowBrush"]; +// } + +// void UpdateBackButton() +// { +// if (_currentPage == null) +// { +// return; +// } + +// bool showBackButton = VirtualView.InternalChildren.Count > 1 && NavigationPage.GetHasBackButton(_currentPage); +// if (NativeVersion.IsDesktop) +// { +// //TODO MAUI: this means it's running as a desktop app +// } +// else +// { +// var navManager = SystemNavigationManager.GetForCurrentView(); +// if(navManager != null) +// navManager.AppViewBackButtonVisibility = showBackButton ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed; +// } + +// NativeView.SetBackButtonTitle(VirtualView); +// } + +// void UpdateTitleOnParents() +// { +// if (VirtualView == null || _currentPage == null) +// return; + +// ITitleProvider? render = null; +// if (_parentTabbedPage != null) +// { +// render = _parentTabbedPage.Handler as ITitleProvider; +// if (render != null) +// render.ShowTitle = (_parentTabbedPage.CurrentPage == VirtualView) && NavigationPage.GetHasNavigationBar(_currentPage); +// } + +// if (_parentFlyoutPage != null) +// { +// render = _parentFlyoutPage.Handler as ITitleProvider; +// if (render != null) +// render.ShowTitle = (_parentFlyoutPage.Detail == VirtualView) && NavigationPage.GetHasNavigationBar(_currentPage); +// } + +// if (render != null && render.ShowTitle) +// { +// render.Title = _currentPage.Title; + +// if (!Brush.IsNullOrEmpty(VirtualView.BarBackground)) +// render.BarBackgroundBrush = GetBarBackgroundBrush(VirtualView); +// else +// render.BarBackgroundBrush = GetBarBackgroundColorBrush(); + +// render.BarForegroundBrush = GetBarForegroundBrush(VirtualView); +// } + +// if (_showTitle || (render != null && render.ShowTitle)) +// { +// ToolbarManager.UpdateToolbarItems(VirtualView) +// .FireAndForget((e)=> Log.Warning(nameof(NavigationPage), $"{e}")); +// } +// } + +// void UpdatePadding() +// { +// NativeView.TitleInset = VirtualView.Padding.Left; +// } + +// void UpdateTitleColor() +// { +// (this as ITitleProvider).BarForegroundBrush = GetBarForegroundBrush(VirtualView); +// } + +// void UpdateNavigationBarBackground() +// { +// (this as ITitleProvider).BarBackgroundBrush = GetBarBackgroundBrush(VirtualView); +// } + +// void UpdateTitleIcon() => +// UpdateTitleIconAsync() +// .FireAndForget(errorCallback: (e) => Log.Warning(nameof(TitleIcon), $"{e}")); + +// async Task UpdateTitleIconAsync() +// { +// var page = _currentPage; +// if (page == null) +// return; + +// ImageSource source = NavigationPage.GetTitleIconImageSource(page); + +// TitleIcon = await source.ToWindowsImageSourceAsync(); + +// if (NativeView == null || _currentPage != page) +// return; + +// NativeView.TitleIcon = TitleIcon; + +// if (_parentFlyoutPage != null && this is ITitleIconProvider parent) +// parent.TitleIcon = TitleIcon; + +// NativeView.UpdateLayout(); +// UpdateContainerArea(); +// } + +// void UpdateTitleView() +// { +// // if the life cycle hasn't reached the point where _parentFlyoutPage gets wired up then +// // don't update the title view +// if (_currentPage == null || !_parentsLookedUp) +// return; + +// // If the container TitleView gets initialized before the FP TitleView it causes the +// // FP TitleView to not render correctly +// if (_parentFlyoutPage != null) +// { +// if (this is ITitleViewProvider parent) +// parent.TitleView = TitleView; +// } +// else if (_parentFlyoutPage == null) +// NativeView.TitleView = TitleView; + +// } + +// void UpdateToolbarPlacement() +// { +// if (NativeView == null) +// { +// return; +// } + +// NativeView.ToolbarPlacement = VirtualView.OnThisPlatform().GetToolbarPlacement(); +// } + +// void UpdateToolbarDynamicOverflowEnabled() +// { +// if (NativeView == null) +// { +// return; +// } + +// NativeView.ToolbarDynamicOverflowEnabled = VirtualView.OnThisPlatform().GetToolbarDynamicOverflowEnabled(); +// } + +// public static void MapPadding(NavigationPageHandler handler, NavigationPage view) => +// handler.UpdatePadding(); + +// public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage view) => handler.UpdateTitleColor(); + +// public static void MapBarBackground(NavigationPageHandler handler, NavigationPage view) => handler.UpdateNavigationBarBackground(); + +// // TODO MAUI: Task Based Mappers? +// public static void MapTitleIcon(NavigationPageHandler handler, NavigationPage view) => handler.UpdateTitleIcon(); + +// public static void MapTitleView(NavigationPageHandler handler, NavigationPage view) => handler.UpdateTitleView(); + +// public static void MapToolbarPlacement(NavigationPageHandler handler, NavigationPage view) => handler.UpdateToolbarPlacement(); + +// public static void MapToolbarDynamicOverflowEnabled(NavigationPageHandler handler, NavigationPage view) => handler.UpdateToolbarDynamicOverflowEnabled(); +// } +//} diff --git a/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.cs b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.cs new file mode 100644 index 000000000000..797b1739f021 --- /dev/null +++ b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Maui.Handlers; + + +namespace Microsoft.Maui.Handlers +{ + internal partial class NavigationPageHandler + { + public static PropertyMapper NavigationPageMapper + = new PropertyMapper(ViewHandler.ViewMapper) + { + //[NavigationPage.BarTextColorProperty.PropertyName] = MapBarTextColor, + //[NavigationPage.BarBackgroundColorProperty.PropertyName] = MapBarBackground, + //[NavigationPage.BarBackgroundProperty.PropertyName] = MapBarBackground, + //[nameof(IPadding.Padding)] = MapPadding, + //[nameof(NavigationPage.TitleIconImageSourceProperty.PropertyName)] = MapTitleIcon, + //[nameof(NavigationPage.TitleViewProperty.PropertyName)] = MapTitleView, + +#if WINDOWS + //[nameof(ToolbarPlacementProperty.PropertyName)] = MapToolbarPlacement, + //[nameof(ToolbarDynamicOverflowEnabledProperty.PropertyName)] = MapToolbarDynamicOverflowEnabled, +#endif + }; + + public static CommandMapper NavigationViewCommandMapper = new(ViewCommandMapper) + { + [nameof(INavigationView.PushAsync)] = PushAsyncTo, + [nameof(INavigationView.PopAsync)] = PopAsyncTo + }; + + public NavigationPageHandler() : base(NavigationPageMapper, NavigationViewCommandMapper) + { + } + + public NavigationPageHandler(PropertyMapper? mapper = null) : base(mapper ?? NavigationPageMapper, NavigationViewCommandMapper) + { + + } + } +} diff --git a/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.iOS.cs b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.iOS.cs new file mode 100644 index 000000000000..50bdb30c2812 --- /dev/null +++ b/src/Core/src/Handlers/NavigationPage/NavigationPageHandler.iOS.cs @@ -0,0 +1,248 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Maui.Handlers; +using UIKit; + +namespace Microsoft.Maui.Handlers +{ + internal partial class NavigationPageHandler : + ViewHandler, INativeViewHandler + { + ControlsNavigationController? _controlsNavigationController; + UIViewController? INativeViewHandler.ViewController => _controlsNavigationController; + + protected override UIView CreateNativeView() + { + _controlsNavigationController = new ControlsNavigationController(this); + + if (_controlsNavigationController.View == null) + throw new NullReferenceException("ControlsNavigationController.View is null"); + + return _controlsNavigationController.View; + } + private static void PushAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + if (arg3 is MauiNavigationRequestedEventArgs args) + arg1.OnPushRequested(args); + } + + private static void PopAsyncTo(NavigationPageHandler arg1, INavigationView arg2, object? arg3) + { + if (arg3 is MauiNavigationRequestedEventArgs args) + arg1.OnPopRequested(args); + } + + void OnPushRequested(MauiNavigationRequestedEventArgs e) + { + _controlsNavigationController? + .OnPushRequested(e, this.MauiContext!); + } + + void OnPopRequested(MauiNavigationRequestedEventArgs e) + { + _controlsNavigationController? + .OnPopRequestedAsync(e) + .FireAndForget((exc) => { }); + } + + internal void SendPopping(Task popTask) + { + if (VirtualView == null) + return; + + // TODO MAUI + VirtualView + .PopAsync() + .FireAndForget((e) => + { + //Log.Warning(nameof(NavigationPageHandler), $"{e}"); + }); + } + + protected override void ConnectHandler(UIView nativeView) + { + base.ConnectHandler(nativeView); + + if (VirtualView == null || MauiContext == null || _controlsNavigationController == null) + return; + + _controlsNavigationController.LoadPages(this.MauiContext); + } + + //public static void MapPadding(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapTitleIcon(NavigationPageHandler handler, INavigationView view) { } + + //public static void MapTitleView(NavigationPageHandler handler, INavigationView view) { } + + ////public static void MapBarBackground(NavigationPageHandler handler, INavigationView view) + ////{ + //// var NavPage = handler.VirtualView; + //// var barBackgroundBrush = NavPage.BarBackground; + + //// if (Brush.IsNullOrEmpty(barBackgroundBrush) && + //// NavPage.BarBackgroundColor != null) + //// barBackgroundBrush = new SolidColorBrush(NavPage.BarBackgroundColor); + + //// if (barBackgroundBrush == null) + //// return; + + //// var navController = handler._controlsNavigationController; + //// var NavigationBar = navController.NavigationBar; + + //// if (NativeVersion.IsAtLeast(13)) + //// { + //// var navigationBarAppearance = NavigationBar.StandardAppearance; + + //// navigationBarAppearance.ConfigureWithOpaqueBackground(); + + //// //if (barBackgroundColor == null) + //// //{ + //// // navigationBarAppearance.BackgroundColor = ColorExtensions.BackgroundColor; + + //// // var parentingViewController = GetParentingViewController(); + //// // parentingViewController?.SetupDefaultNavigationBarAppearance(); + //// //} + //// //else + //// // navigationBarAppearance.BackgroundColor = barBackgroundColor.ToUIColor(); + + //// var backgroundImage = NavigationBar.GetBackgroundImage(barBackgroundBrush); + //// navigationBarAppearance.BackgroundImage = backgroundImage; + + //// NavigationBar.CompactAppearance = navigationBarAppearance; + //// NavigationBar.StandardAppearance = navigationBarAppearance; + //// NavigationBar.ScrollEdgeAppearance = navigationBarAppearance; + //// } + //// else + //// { + //// var backgroundImage = NavigationBar.GetBackgroundImage(barBackgroundBrush); + //// NavigationBar.SetBackgroundImage(backgroundImage, UIBarMetrics.Default); + //// } + ////} + + ////public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage view) + ////{ + //// var NavPage = handler.VirtualView; + + //// var navController = handler._controlsNavigationController; + //// var NavigationBar = navController.NavigationBar; + + //// var barTextColor = NavPage.BarTextColor; + //// if (NavigationBar == null) + //// return; + + //// // Determine new title text attributes via global static data + //// var globalTitleTextAttributes = UINavigationBar.Appearance.TitleTextAttributes; + //// var titleTextAttributes = new UIStringAttributes + //// { + //// ForegroundColor = barTextColor == null ? globalTitleTextAttributes?.ForegroundColor : barTextColor.ToNative(), + //// Font = globalTitleTextAttributes?.Font + //// }; + + //// // Determine new large title text attributes via global static data + //// var largeTitleTextAttributes = titleTextAttributes; + //// if (NativeVersion.IsAtLeast(11)) + //// { + //// var globalLargeTitleTextAttributes = UINavigationBar.Appearance.LargeTitleTextAttributes; + + //// largeTitleTextAttributes = new UIStringAttributes + //// { + //// ForegroundColor = barTextColor == null ? globalLargeTitleTextAttributes?.ForegroundColor : barTextColor.ToNative(), + //// Font = globalLargeTitleTextAttributes?.Font + //// }; + //// } + + //// if (NativeVersion.IsAtLeast(13)) + //// { + //// if (NavigationBar.CompactAppearance != null) + //// { + //// NavigationBar.CompactAppearance.TitleTextAttributes = titleTextAttributes; + //// NavigationBar.CompactAppearance.LargeTitleTextAttributes = largeTitleTextAttributes; + //// } + + //// NavigationBar.StandardAppearance.TitleTextAttributes = titleTextAttributes; + //// NavigationBar.StandardAppearance.LargeTitleTextAttributes = largeTitleTextAttributes; + + //// if (NavigationBar.ScrollEdgeAppearance != null) + //// { + //// NavigationBar.ScrollEdgeAppearance.TitleTextAttributes = titleTextAttributes; + //// NavigationBar.ScrollEdgeAppearance.LargeTitleTextAttributes = largeTitleTextAttributes; + //// } + //// } + //// else + //// { + //// NavigationBar.TitleTextAttributes = titleTextAttributes; + + //// if (NativeVersion.IsAtLeast(11)) + //// NavigationBar.LargeTitleTextAttributes = largeTitleTextAttributes; + //// } + + //// //// set Tint color (i. e. Back Button arrow and Text) + //// //var iconColor = Current != null ? NavigationPage.GetIconColor(Current) : null; + //// //if (iconColor == null) + //// // iconColor = barTextColor; + + //// //NavigationBar.TintColor = iconColor == null || NavPage.OnThisPlatform().GetStatusBarTextColorMode() == StatusBarTextColorMode.DoNotAdjust + //// // ? UINavigationBar.Appearance.TintColor + //// // : iconColor.ToUIColor(); + ////} + + + //protected override void ConnectHandler(UIView nativeView) + //{ + // base.ConnectHandler(nativeView); + + // if (VirtualView == null) + // return; + + // VirtualView.PushRequested += OnPushRequested; + // VirtualView.PopRequested += OnPopRequested; + // _controlsNavigationController.LoadPages(this.MauiContext); + + // //VirtualView.PopToRootRequested += OnPopToRootRequested; + // //VirtualView.RemovePageRequested += OnRemovedPageRequested; + // //VirtualView.InsertPageBeforeRequested += OnInsertPageBeforeRequested; + //} + + //protected override void DisconnectHandler(UIView nativeView) + //{ + // base.DisconnectHandler(nativeView); + + // if (VirtualView == null) + // return; + + // VirtualView.PushRequested -= OnPushRequested; + // VirtualView.PopRequested -= OnPopRequested; + // //VirtualView.PopToRootRequested -= OnPopToRootRequested; + // //VirtualView.RemovePageRequested -= OnRemovedPageRequested; + // //VirtualView.InsertPageBeforeRequested -= OnInsertPageBeforeRequested; + //} + + //void OnPushRequested(object? sender, NavigationRequestedEventArgs e) + //{ + // _controlsNavigationController? + // .OnPushRequested(e, this.MauiContext); + //} + + //void OnPopRequested(object? sender, NavigationRequestedEventArgs e) + //{ + // _controlsNavigationController? + // .OnPopRequestedAsync(e) + // .FireAndForget((exc) => Log.Warning(nameof(NavigationPage), $"{exc}")); + //} + + //internal void SendPopping(Task popTask) + //{ + // if (VirtualView == null) + // return; + + // VirtualView.PopAsyncInner(false, true, true) + // .FireAndForget((exc) => Log.Warning(nameof(NavigationPage), $"{exc}")); + //} + } +} diff --git a/src/Controls/src/Core/Platform/Android/MauiFragmentNavDestination.cs b/src/Core/src/Platform/Android/MauiFragmentNavDestination.cs similarity index 81% rename from src/Controls/src/Core/Platform/Android/MauiFragmentNavDestination.cs rename to src/Core/src/Platform/Android/MauiFragmentNavDestination.cs index a8bde1bc8182..868cc2a307ad 100644 --- a/src/Controls/src/Core/Platform/Android/MauiFragmentNavDestination.cs +++ b/src/Core/src/Platform/Android/MauiFragmentNavDestination.cs @@ -3,21 +3,21 @@ using System.Text; using AndroidX.Navigation; using AndroidX.Navigation.Fragment; -using Microsoft.Maui.Controls.Handlers; +using Microsoft.Maui.Handlers; -namespace Microsoft.Maui.Controls.Platform +namespace Microsoft.Maui { class MauiFragmentNavDestination : FragmentNavigator.Destination { - public IPage Page { get; } - public IMauiContext MauiContext => NavigationPageHandler.MauiContext; + public IView Page { get; } + public IMauiContext MauiContext => NavigationPageHandler.MauiContext ?? throw new InvalidOperationException($"MauiContext cannot be null here"); public NavigationPageHandler NavigationPageHandler { get; } // Todo we want to generate the same ids for each page so if the app is recreated // we want these to match up - static Dictionary Pages = new Dictionary(); + static Dictionary Pages = new Dictionary(); - public MauiFragmentNavDestination(Navigator fragmentNavigator, IPage page, NavigationPageHandler navigationPageHandler) : base(fragmentNavigator) + public MauiFragmentNavDestination(Navigator fragmentNavigator, IView page, NavigationPageHandler navigationPageHandler) : base(fragmentNavigator) { _ = page ?? throw new ArgumentNullException(nameof(page)); _ = navigationPageHandler ?? throw new ArgumentNullException(nameof(navigationPageHandler)); @@ -35,7 +35,7 @@ public MauiFragmentNavDestination(Navigator fragmentNavigator, IPage page, Navig } public static MauiFragmentNavDestination AddDestination( - IPage page, + IView page, NavigationPageHandler navigationPageHandler, NavGraph navGraph, FragmentNavigator navigator) diff --git a/src/Controls/src/Core/Platform/Android/NavHostPageFragment.cs b/src/Core/src/Platform/Android/NavHostPageFragment.cs similarity index 82% rename from src/Controls/src/Core/Platform/Android/NavHostPageFragment.cs rename to src/Core/src/Platform/Android/NavHostPageFragment.cs index c2e066f9de8c..03d6c9a9bfa9 100644 --- a/src/Controls/src/Core/Platform/Android/NavHostPageFragment.cs +++ b/src/Core/src/Platform/Android/NavHostPageFragment.cs @@ -12,22 +12,26 @@ using AndroidX.Fragment.App; using AndroidX.Navigation.Fragment; using AndroidX.Navigation.UI; -using Microsoft.Maui.Controls.Handlers; +using Microsoft.Maui.Handlers; using AView = Android.Views.View; -namespace Microsoft.Maui.Controls.Platform +namespace Microsoft.Maui { class NavHostPageFragment : Fragment { + private MauiFragmentNavDestination? _navDestination; + ProcessBackClick BackClick { get; } NavHostFragment NavHost => - (NavHostFragment) - Context - .GetFragmentManager() - .FindFragmentById(Resource.Id.nav_host); + (Context?.GetFragmentManager()?.FindFragmentById(Resource.Id.nav_host) + as NavHostFragment) ?? throw new InvalidOperationException($"NavHost cannot be null here"); - MauiFragmentNavDestination NavDestination { get; set; } + MauiFragmentNavDestination NavDestination + { + get => _navDestination ?? throw new InvalidOperationException($"NavDestination cannot be null here"); + set => _navDestination = value; + } protected NavHostPageFragment(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { @@ -41,7 +45,7 @@ public NavHostPageFragment() public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - if (NavDestination == null) + if (_navDestination == null) { NavDestination = (MauiFragmentNavDestination) @@ -73,16 +77,16 @@ public override void OnViewCreated(AView view, Bundle savedInstanceState) UpdateToolbar(); NavDestination.NavigationPageHandler.Toolbar - .Title = NavDestination.Page.Title; + .Title = (NavDestination.Page as IPage)?.Title; if (Context.GetActivity() is AppCompatActivity aca) { - aca.SupportActionBar.Title = NavDestination.Page.Title; + aca.SupportActionBar.Title = (NavDestination.Page as IPage)?.Title; // TODO MAUI put this elsewhere once we figure out how attached property handlers work - bool showNavBar = false; - if (NavDestination.Page is BindableObject bo) - showNavBar = NavigationPage.GetHasNavigationBar(bo); + bool showNavBar = true; + //if (NavDestination.Page is BindableObject bo) + // showNavBar = NavigationPage.GetHasNavigationBar(bo); var appBar = NavDestination.NavigationPageHandler.AppBar; if (!showNavBar) @@ -161,7 +165,7 @@ public override void HandleOnBackPressed() _navHostPageFragment.HandleOnBackPressed(); } - public void OnClick(AView v) + public void OnClick(AView? v) { HandleOnBackPressed(); } diff --git a/src/Controls/src/Core/Platform/iOS/ControlsNavigationController.cs b/src/Core/src/Platform/iOS/ControlsNavigationController.cs similarity index 80% rename from src/Controls/src/Core/Platform/iOS/ControlsNavigationController.cs rename to src/Core/src/Platform/iOS/ControlsNavigationController.cs index bd78fa299ad5..69ca1831e045 100644 --- a/src/Controls/src/Core/Platform/iOS/ControlsNavigationController.cs +++ b/src/Core/src/Platform/iOS/ControlsNavigationController.cs @@ -4,26 +4,24 @@ using System.Linq; using System.Threading.Tasks; using Foundation; -using Microsoft.Maui.Controls.Handlers; -using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Handlers; using ObjCRuntime; using UIKit; -namespace Microsoft.Maui.Controls.Platform +namespace Microsoft.Maui { - public class ControlsNavigationController : UINavigationController + internal class ControlsNavigationController : UINavigationController { readonly NavigationPageHandler _handler; Dictionary> _completionTasks = new Dictionary>(); - TaskCompletionSource _popCompletionTask; + TaskCompletionSource? _popCompletionTask; // This holds the view controllers for each page - readonly Dictionary _trackers = - new Dictionary(); + readonly Dictionary _trackers = + new Dictionary(); - IReadOnlyList NavigationStack => _handler.VirtualView.Navigation.NavigationStack; + IReadOnlyList NavigationStack => _handler.VirtualView.NavigationStack; public ControlsNavigationController(NavigationPageHandler handler) : base() { @@ -39,12 +37,14 @@ public ControlsNavigationController(NavigationPageHandler handler, Type navigati } [Export("navigationBar:shouldPopItem:")] - [Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)] public bool ShouldPopItem(UINavigationBar navigationBar, UINavigationItem item) => SendPop(); internal bool SendPop() { + if (ViewControllers == null) + return false; + // this means the pop is already done, nothing we can do if (ViewControllers.Length < NavigationBar.Items.Length) return true; @@ -106,6 +106,7 @@ async void SendPoppedOnCompletion(Task popTask) } var poppedPage = NavigationStack[NavigationStack.Count - 1]; + _handler.SendPopping(popTask); await popTask; @@ -113,18 +114,24 @@ async void SendPoppedOnCompletion(Task popTask) DisposePage(poppedPage); } - void DisposePage(IPage page, bool calledFromDispose = false) + void DisposePage(IView page, bool calledFromDispose = false) { + if (ViewControllers == null) + return; + if (_trackers.TryGetValue(page, out var tracker)) { - if (!calledFromDispose && tracker.ViewController != null && ViewControllers.Contains(tracker.ViewController)) - ViewControllers = ViewControllers.Remove(_trackers[page].ViewController); + if (!calledFromDispose && tracker.ViewController != null && ViewControllers.Contains(tracker.ViewController) && + _trackers[page].ViewController != null) + { + ViewControllers = ViewControllers.Remove(_trackers[page].ViewController!); + } _trackers.Remove(page); } } - internal async Task OnPopRequestedAsync(NavigationRequestedEventArgs e) + internal async Task OnPopRequestedAsync(MauiNavigationRequestedEventArgs e) { var page = e.Page; var animated = e.Animated; @@ -145,7 +152,7 @@ internal void LoadPages(IMauiContext mauiContext) PushPage(page, false, mauiContext); } - internal void OnPushRequested(NavigationRequestedEventArgs e, IMauiContext mauiContext) + internal void OnPushRequested(MauiNavigationRequestedEventArgs e, IMauiContext mauiContext) { var page = e.Page; var animated = e.Animated; @@ -157,10 +164,10 @@ internal void OnPushRequested(NavigationRequestedEventArgs e, IMauiContext mauiC } - void PushPage(IPage page, bool animated, IMauiContext mauiContext, TaskCompletionSource completionSource = null) + void PushPage(IView page, bool animated, IMauiContext mauiContext, TaskCompletionSource? completionSource = null) { var viewController = page.ToUIViewController(mauiContext); - var handler = (INativeViewHandler)page.Handler; + var handler = (INativeViewHandler)page.Handler!; _trackers[page] = handler; @@ -170,7 +177,7 @@ void PushPage(IPage page, bool animated, IMauiContext mauiContext, TaskCompletio PushViewController(viewController, animated); } - IPage ElementForViewController(UIViewController viewController) + IView? ElementForViewController(UIViewController viewController) { foreach (var child in _trackers) { @@ -196,7 +203,7 @@ public NavDelegate(ControlsNavigationController renderer) // https://github.com/xamarin/Microsoft.Maui.Controls.Compatibility/issues/10519 [Export("navigationController:animationControllerForOperation:fromViewController:toViewController:")] [Foundation.Preserve(Conditional = true)] - public new IUIViewControllerAnimatedTransitioning GetAnimationControllerForOperation(UINavigationController navigationController, UINavigationControllerOperation operation, UIViewController fromViewController, UIViewController toViewController) + public new IUIViewControllerAnimatedTransitioning? GetAnimationControllerForOperation(UINavigationController navigationController, UINavigationControllerOperation operation, UIViewController fromViewController, UIViewController toViewController) { return null; } @@ -223,8 +230,8 @@ public override void WillShowViewController(UINavigationController navigationCon bool navBarVisible = true; - if (element is BindableObject bo) - navBarVisible = NavigationPage.GetHasNavigationBar(bo); + //if (element is BindableObject bo) + // navBarVisible = NavigationPage.GetHasNavigationBar(bo); navigationController.SetNavigationBarHidden(!navBarVisible, true); diff --git a/src/Core/src/Primitives/NavigationRequestedEventArgs.cs b/src/Core/src/Primitives/NavigationRequestedEventArgs.cs new file mode 100644 index 000000000000..7d1e8296948c --- /dev/null +++ b/src/Core/src/Primitives/NavigationRequestedEventArgs.cs @@ -0,0 +1,38 @@ +using System; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace Microsoft.Maui +{ + internal class MauiNavigationEventArgs : EventArgs + { + public MauiNavigationEventArgs(IView page) + { + if (page == null) + throw new ArgumentNullException("page"); + + Page = page; + } + + public IView Page { get; } + } + + internal class MauiNavigationRequestedEventArgs : MauiNavigationEventArgs + { + public MauiNavigationRequestedEventArgs(IView page, bool animated) : base(page) + { + Animated = animated; + } + + public MauiNavigationRequestedEventArgs(IView page, IView before, bool animated) : this(page, animated) + { + BeforePage = before; + } + + public bool Animated { get; set; } + + public IView? BeforePage { get; set; } + + public Task? Task { get; set; } + } +} \ No newline at end of file