From 78714366c0a261bfdb4ae37fdd6d76cf98e86b19 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Thu, 28 Nov 2024 18:33:07 +0000 Subject: [PATCH] Ensure last breadcrumb is always visible and always animates in (#18) --- Scr/Breadcrumb.xaml.cs | 57 +++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/Scr/Breadcrumb.xaml.cs b/Scr/Breadcrumb.xaml.cs index ceb4cce..23e5cfc 100644 --- a/Scr/Breadcrumb.xaml.cs +++ b/Scr/Breadcrumb.xaml.cs @@ -183,6 +183,13 @@ async void BreadCrumbContainer_Loaded(object? sender, EventArgs e) continue; } + Border invisibleBreadcrumb = BreadcrumbCreator(page, page.Equals(pages.LastOrDefault()), page.Equals(pages.FirstOrDefault())); + invisibleBreadcrumb.Opacity = 0; + AutomationProperties.SetIsInAccessibleTree(invisibleBreadcrumb, false); + AutomationProperties.SetExcludedWithChildren(invisibleBreadcrumb, true); + + BreadCrumbContainer.Children.Add(invisibleBreadcrumb); + if(BreadCrumbContainer.Children.Count > 0) { await BreadCrumbsScrollView.ScrollToAsync((View?)BreadCrumbContainer.Children.LastOrDefault(), ScrollToPosition.MakeVisible, false); @@ -195,7 +202,7 @@ async void BreadCrumbContainer_Loaded(object? sender, EventArgs e) breadcrumb.TranslationX = Application.Current?.Windows[0].Page?.Width ?? 0; // Scroll to end of control - await Task.Delay(10); + await Task.Delay(100); // HACK: Remove once fixed - https://github.com/dotnet/maui/issues/9446 if(BreadCrumbContainer.Width < BreadCrumbsScrollView.Width) @@ -268,21 +275,47 @@ Border BreadcrumbCreator(Page page, bool isLast, bool isFirst) /// async void AnimatedStack_ChildAdded(object? sender, ElementEventArgs e) { - // iOS scroll to end fix - if(DeviceInfo.Platform.Equals(DevicePlatform.iOS)) + await Task.Run(async () => { - await BreadCrumbsScrollView.ScrollToAsync((View?)BreadCrumbContainer.Children.LastOrDefault(), ScrollToPosition.MakeVisible, false); - } + while(BreadCrumbContainer.Children.LastOrDefault() is not View lastBreadcrumb || lastBreadcrumb.Width <= 0) + { + await Task.Delay(100); + } - Animation lastBreadcrumbAnimation = new() - { - { 0, 1, new Animation(_ => ((View)BreadCrumbContainer.Children[^1]).TranslationX = _, Application.Current?.Windows[0].Page?.Width ?? 0, 0, Easing.Linear) } - }; + // iOS scroll to end fix + if(DeviceInfo.Platform.Equals(DevicePlatform.iOS)) + { + MainThread.BeginInvokeOnMainThread(async () => await BreadCrumbsScrollView.ScrollToAsync((View?)BreadCrumbContainer.Children.LastOrDefault(), ScrollToPosition.MakeVisible, false)); + } + + double lastBreadcrumbWidth = ((View)BreadCrumbContainer.Children[^1]).Width; + Animation lastBreadcrumbAnimation = new() + { + { 0, 1, new Animation(_ => ((View)BreadCrumbContainer.Children[^1]).TranslationX = _, Application.Current?.Windows[0].Page?.Width - lastBreadcrumbWidth ?? 0, lastBreadcrumbWidth * -1, Easing.Linear) } + }; + + Point point = BreadCrumbsScrollView.GetScrollPositionForElement((View)BreadCrumbContainer.Children[^1], ScrollToPosition.End); + lastBreadcrumbAnimation.Add(0, 1, new Animation(_ => BreadCrumbsScrollView.ScrollToAsync((View?)BreadCrumbContainer.Children.LastOrDefault(), ScrollToPosition.MakeVisible, true), BreadCrumbsScrollView.X, point.X - 6)); - Point point = BreadCrumbsScrollView.GetScrollPositionForElement((View)BreadCrumbContainer.Children[^1], ScrollToPosition.End); - lastBreadcrumbAnimation.Add(0, 1, new Animation(_ => BreadCrumbsScrollView.ScrollToAsync((View?)BreadCrumbContainer.Children.LastOrDefault(), ScrollToPosition.MakeVisible, true), BreadCrumbsScrollView.X, point.X - 6)); + MainThread.BeginInvokeOnMainThread(() => lastBreadcrumbAnimation.Commit(this, nameof(lastBreadcrumbAnimation), 16, AnimationSpeed)); + + double containerWidth = 0; + foreach(View view in BreadCrumbContainer.Children.Cast()) + { + containerWidth += view.Width; + } + containerWidth -= lastBreadcrumbWidth; - lastBreadcrumbAnimation.Commit(this, nameof(lastBreadcrumbAnimation), 16, AnimationSpeed); + if(containerWidth > BreadCrumbsScrollView.Width) + { + MainThread.BeginInvokeOnMainThread(() => BreadCrumbContainer.WidthRequest = containerWidth); + } + else + { + MainThread.BeginInvokeOnMainThread(() => BreadCrumbContainer.WidthRequest = BreadCrumbsScrollView.Width); + } + MainThread.BeginInvokeOnMainThread(async () => await BreadCrumbsScrollView.ScrollToAsync((View?)BreadCrumbContainer.Children.LastOrDefault(), ScrollToPosition.MakeVisible, false)); + }); } ///