Calculate Nav Source and fix popping params (#12514)
* Calculate Nav Source and fix popping params * Update Shell.cs * - fix length check * - poptoroot * Update Shell.cs * Update Shell.cs * Update Shell.cs * - fix inserting middle pages and a few events
This commit is contained in:
Родитель
65609dca81
Коммит
3b5b760f41
|
@ -172,6 +172,25 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
Is.EqualTo($"//{itemRoute}/{Routing.GetRoute(page1)}"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertTwoPagesAtSeparatePoints()
|
||||
{
|
||||
Routing.RegisterRoute("pagefirstmiddle", typeof(ContentPage));
|
||||
Routing.RegisterRoute("pagesecondmiddle", typeof(ContentPage));
|
||||
Routing.RegisterRoute("last", typeof(ContentPage));
|
||||
Routing.RegisterRoute("middle", typeof(ContentPage));
|
||||
|
||||
var shell = new TestShell(
|
||||
CreateShellItem(shellItemRoute: "item")
|
||||
);
|
||||
|
||||
await shell.GoToAsync("//item/middle/last");
|
||||
await shell.GoToAsync("//item/pagefirstmiddle/middle/pagesecondmiddle/last");
|
||||
|
||||
Assert.That(shell.CurrentState.Location.ToString(),
|
||||
Is.EqualTo("//item/pagefirstmiddle/middle/pagesecondmiddle/last"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NavigationPushAndPopBasic()
|
||||
{
|
||||
|
@ -367,6 +386,161 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
Assert.AreEqual(3, tab.NavigationsFired.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PoppingSamePageSetsCorrectNavigationSource()
|
||||
{
|
||||
Routing.RegisterRoute("detailspage", typeof(ContentPage));
|
||||
var shell = new TestShell(CreateShellItem(shellItemRoute:"item1"));
|
||||
await shell.GoToAsync("detailspage/detailspage");
|
||||
await shell.Navigation.PopAsync();
|
||||
|
||||
|
||||
shell.TestNavigatingArgs(ShellNavigationSource.Pop,
|
||||
"//item1/detailspage/detailspage", $"..");
|
||||
|
||||
shell.TestNavigatedArgs(ShellNavigationSource.Pop,
|
||||
"//item1/detailspage/detailspage", $"//item1/detailspage");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PoppingSetsCorrectNavigationSource()
|
||||
{
|
||||
var shell = new TestShell(CreateShellItem(shellContentRoute: "item1"));
|
||||
shell.RegisterPage("page1");
|
||||
shell.RegisterPage("page2");
|
||||
|
||||
await shell.GoToAsync("page1");
|
||||
await shell.GoToAsync("page2");
|
||||
await shell.Navigation.PopAsync();
|
||||
|
||||
shell.TestNavigatingArgs(ShellNavigationSource.Pop,
|
||||
"//item1/page1/page2", $"..");
|
||||
|
||||
shell.TestNavigatedArgs(ShellNavigationSource.Pop,
|
||||
"//item1/page1/page2", $"//item1/page1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PopToRootSetsCorrectNavigationSource()
|
||||
{
|
||||
var shell = new TestShell(CreateShellItem());
|
||||
await shell.Navigation.PushAsync(new ContentPage());
|
||||
await shell.Navigation.PushAsync(new ContentPage());
|
||||
await shell.Navigation.PopToRootAsync();
|
||||
Assert.AreEqual(ShellNavigationSource.PopToRoot, shell.LastShellNavigatingEventArgs.Source);
|
||||
|
||||
await shell.Navigation.PushAsync(new ContentPage());
|
||||
await shell.Navigation.PushAsync(new ContentPage());
|
||||
|
||||
await shell.Navigation.PopAsync();
|
||||
Assert.AreEqual(ShellNavigationSource.Pop, shell.LastShellNavigatingEventArgs.Source);
|
||||
|
||||
await shell.Navigation.PopAsync();
|
||||
Assert.AreEqual(ShellNavigationSource.PopToRoot, shell.LastShellNavigatingEventArgs.Source);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PushingSetsCorrectNavigationSource()
|
||||
{
|
||||
var shell = new TestShell(CreateShellItem(shellItemRoute: "item1"));
|
||||
shell.RegisterPage(nameof(PushingSetsCorrectNavigationSource));
|
||||
await shell.GoToAsync(nameof(PushingSetsCorrectNavigationSource));
|
||||
|
||||
shell.TestNavigatingArgs(ShellNavigationSource.Push,
|
||||
"//item1", $"{nameof(PushingSetsCorrectNavigationSource)}");
|
||||
|
||||
shell.TestNavigatedArgs(ShellNavigationSource.Push,
|
||||
"//item1", $"//item1/{nameof(PushingSetsCorrectNavigationSource)}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ChangingShellItemSetsCorrectNavigationSource()
|
||||
{
|
||||
var shell = new TestShell(
|
||||
CreateShellItem(shellItemRoute: "item1"),
|
||||
CreateShellItem(shellItemRoute: "item2")
|
||||
);
|
||||
|
||||
await shell.GoToAsync("//item2");
|
||||
|
||||
shell.TestNavigationArgs(ShellNavigationSource.ShellItemChanged,
|
||||
"//item1", "//item2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ChangingShellSectionSetsCorrectNavigationSource()
|
||||
{
|
||||
var shell = new TestShell(
|
||||
CreateShellItem(shellSectionRoute:"item1")
|
||||
);
|
||||
|
||||
shell.Items[0].Items.Add(CreateShellSection(shellContentRoute: "item2"));
|
||||
|
||||
await shell.GoToAsync("//item2");
|
||||
|
||||
shell.TestNavigationArgs(ShellNavigationSource.ShellSectionChanged,
|
||||
"//item1", "//item2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ChangingShellContentSetsCorrectNavigationSource()
|
||||
{
|
||||
var shell = new TestShell(
|
||||
CreateShellItem(shellContentRoute:"item1")
|
||||
);
|
||||
|
||||
shell.Items[0].Items[0].Items.Add(CreateShellContent(shellContentRoute: "item2"));
|
||||
|
||||
await shell.GoToAsync("//item2");
|
||||
|
||||
shell.TestNavigationArgs(ShellNavigationSource.ShellContentChanged,
|
||||
"//item1", "//item2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InsertPageSetsCorrectNavigationSource()
|
||||
{
|
||||
Routing.RegisterRoute("pagemiddle", typeof(ContentPage));
|
||||
Routing.RegisterRoute("page", typeof(ContentPage));
|
||||
var shell = new TestShell(
|
||||
CreateShellItem(shellItemRoute: "item")
|
||||
);
|
||||
|
||||
await shell.GoToAsync("//item/page");
|
||||
await shell.GoToAsync("//item/pagemiddle/page");
|
||||
|
||||
shell.TestNavigationArgs(ShellNavigationSource.Insert,
|
||||
"//item/page", "//item/pagemiddle/page");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RemovePageSetsCorrectNavigationSource()
|
||||
{
|
||||
Routing.RegisterRoute("pagemiddle", typeof(ContentPage));
|
||||
Routing.RegisterRoute("page", typeof(ContentPage));
|
||||
var shell = new TestShell(
|
||||
CreateShellItem(shellItemRoute: "item")
|
||||
);
|
||||
|
||||
await shell.GoToAsync("//item/pagemiddle/page");
|
||||
await shell.GoToAsync("//item/page");
|
||||
|
||||
|
||||
shell.TestNavigationArgs(ShellNavigationSource.Remove,
|
||||
"//item/pagemiddle/page", "//item/page");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task InitialNavigatingArgs()
|
||||
{
|
||||
var shell = new TestShell(
|
||||
CreateShellItem(shellItemRoute: "item")
|
||||
);
|
||||
|
||||
shell.TestNavigationArgs(ShellNavigationSource.ShellItemChanged,
|
||||
null, "//item");
|
||||
}
|
||||
|
||||
public class NavigationMonitoringTab : Tab
|
||||
{
|
||||
public List<string> NavigationsFired = new List<string>();
|
||||
|
|
|
@ -241,5 +241,65 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
var testPage = (shell.CurrentItem.CurrentItem as IShellSectionController).PresentedPage as ShellTestPage;
|
||||
Assert.AreEqual(1234d, testPage.DoubleQueryParameter);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NavigatingBackDoesntClearParametersFromPreviousPage()
|
||||
{
|
||||
var shell = new TestShell(CreateShellItem());
|
||||
|
||||
Routing.RegisterRoute("details", typeof(ShellTestPage));
|
||||
|
||||
await shell.GoToAsync($"details?{nameof(ShellTestPage.SomeQueryParameter)}=1");
|
||||
await shell.GoToAsync("details");
|
||||
await shell.GoToAsync("..");
|
||||
var testPage = shell.CurrentPage as ShellTestPage;
|
||||
Assert.AreEqual("1", testPage.SomeQueryParameter);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NavigatingBackAbsolutelyClearsParametersFromPreviousPage()
|
||||
{
|
||||
var shell = new TestShell(CreateShellItem(shellItemRoute:"item"));
|
||||
|
||||
Routing.RegisterRoute("details", typeof(ShellTestPage));
|
||||
|
||||
await shell.GoToAsync($"details?{nameof(ShellTestPage.SomeQueryParameter)}=1");
|
||||
await shell.GoToAsync("details");
|
||||
await shell.GoToAsync("//item/details");
|
||||
var testPage = shell.CurrentPage as ShellTestPage;
|
||||
Assert.AreEqual(null, testPage.SomeQueryParameter);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NavigatingBackToShellContentRetainsQueryParameter()
|
||||
{
|
||||
var shell = new Shell();
|
||||
ShellTestPage pagetoTest = new ShellTestPage();
|
||||
pagetoTest.BindingContext = pagetoTest;
|
||||
var one = CreateShellItem(pagetoTest, shellContentRoute: "content");
|
||||
shell.Items.Add(one);
|
||||
ShellTestPage page = (ShellTestPage)shell.CurrentPage;
|
||||
await shell.GoToAsync($"//content?{nameof(ShellTestPage.SomeQueryParameter)}=1234");
|
||||
await shell.Navigation.PushAsync(new ContentPage());
|
||||
await shell.Navigation.PopAsync();
|
||||
Assert.AreEqual("1234", page.SomeQueryParameter);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NavigatingBackToShellContentAbsolutelyClearsQueryParameter()
|
||||
{
|
||||
var shell = new Shell();
|
||||
ShellTestPage pagetoTest = new ShellTestPage();
|
||||
pagetoTest.BindingContext = pagetoTest;
|
||||
var one = CreateShellItem(pagetoTest, shellContentRoute: "content");
|
||||
shell.Items.Add(one);
|
||||
ShellTestPage page = (ShellTestPage)shell.CurrentPage;
|
||||
await shell.GoToAsync($"//content?{nameof(ShellTestPage.SomeQueryParameter)}=1234");
|
||||
await shell.Navigation.PushAsync(new ContentPage());
|
||||
await shell.GoToAsync($"//content");
|
||||
Assert.AreEqual(null, page.SomeQueryParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -352,6 +352,38 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
OnNavigatingCount++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void TestNavigationArgs(ShellNavigationSource source, string from, string to)
|
||||
{
|
||||
TestNavigatingArgs(source, from, to);
|
||||
TestNavigatedArgs(source, from, to);
|
||||
}
|
||||
|
||||
public void TestNavigatedArgs(ShellNavigationSource source, string from, string to)
|
||||
{
|
||||
Assert.AreEqual(source, this.LastShellNavigatedEventArgs.Source);
|
||||
|
||||
if (from == null)
|
||||
Assert.AreEqual(LastShellNavigatedEventArgs.Previous, null);
|
||||
else
|
||||
Assert.AreEqual(from, this.LastShellNavigatedEventArgs.Previous.Location.ToString());
|
||||
|
||||
Assert.AreEqual(to, this.LastShellNavigatedEventArgs.Current.Location.ToString());
|
||||
}
|
||||
|
||||
public void TestNavigatingArgs(ShellNavigationSource source, string from, string to)
|
||||
{
|
||||
Assert.AreEqual(source, this.LastShellNavigatingEventArgs.Source);
|
||||
|
||||
if (from == null)
|
||||
Assert.AreEqual(LastShellNavigatingEventArgs.Current, null);
|
||||
else
|
||||
Assert.AreEqual(from, this.LastShellNavigatingEventArgs.Current.Location.ToString());
|
||||
|
||||
Assert.AreEqual(to, this.LastShellNavigatingEventArgs.Target.Location.ToString());
|
||||
}
|
||||
|
||||
public Func<bool> OnBackButtonPressedFunc;
|
||||
protected override bool OnBackButtonPressed()
|
||||
{
|
||||
|
|
|
@ -449,36 +449,6 @@ namespace Xamarin.Forms
|
|||
|
||||
public static Shell Current => Application.Current?.MainPage as Shell;
|
||||
|
||||
|
||||
List<RequestDefinition> BuildAllTheRoutes()
|
||||
{
|
||||
List<RequestDefinition> routes = new List<RequestDefinition>();
|
||||
// todo make better maybe
|
||||
|
||||
for (var i = 0; i < Items.Count; i++)
|
||||
{
|
||||
var item = Items[i];
|
||||
|
||||
for (var j = 0; j < item.Items.Count; j++)
|
||||
{
|
||||
var section = item.Items[j];
|
||||
|
||||
for (var k = 0; k < section.Items.Count; k++)
|
||||
{
|
||||
var content = section.Items[k];
|
||||
|
||||
string longUri = $"{RouteScheme}://{RouteHost}/{Routing.GetRoute(this)}/{Routing.GetRoute(item)}/{Routing.GetRoute(section)}/{Routing.GetRoute(content)}";
|
||||
|
||||
longUri = longUri.TrimEnd('/');
|
||||
|
||||
routes.Add(new RequestDefinition(longUri, item, section, content, new List<string>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
internal Task CompleteDeferredNavigating(ShellNavigatingEventArgs deferredArgs)
|
||||
{
|
||||
return GoToAsync(deferredArgs.Target, deferredArgs.Animate, false, deferredArgs);
|
||||
|
@ -520,8 +490,12 @@ namespace Xamarin.Forms
|
|||
throw new InvalidOperationException("Not all ShellNavigatingDeferrals have been completed from the previous operation");
|
||||
}
|
||||
|
||||
// FIXME: This should not be none, we need to compute the delta and set flags correctly
|
||||
var accept = ProposeNavigation(ShellNavigationSource.Unknown, state, this.CurrentState != null, deferredArgs);
|
||||
var navigationRequest = ShellUriHandler.GetNavigationRequest(this, state.FullLocation, enableRelativeShellRoutes, shellNavigationParameters: shellNavigationParameters);
|
||||
bool isRelativePopping = ShellUriHandler.IsTargetRelativePop(shellNavigationParameters);
|
||||
|
||||
ShellNavigationSource source = CalculateNavigationSource(CurrentState, navigationRequest);
|
||||
|
||||
var accept = ProposeNavigation(source, state, this.CurrentState != null, deferredArgs);
|
||||
|
||||
if (deferredArgs == null && _deferredEventArgs != null)
|
||||
{
|
||||
|
@ -539,12 +513,11 @@ namespace Xamarin.Forms
|
|||
|
||||
_accumulateNavigatedEvents = true;
|
||||
|
||||
var navigationRequest = ShellUriHandler.GetNavigationRequest(this, state.FullLocation, enableRelativeShellRoutes, shellNavigationParameters: shellNavigationParameters);
|
||||
var uri = navigationRequest.Request.FullUri;
|
||||
var queryString = navigationRequest.Query;
|
||||
var queryData = ParseQueryString(queryString);
|
||||
|
||||
ApplyQueryAttributes(this, queryData, false);
|
||||
ApplyQueryAttributes(this, queryData, false, false);
|
||||
|
||||
var shellItem = navigationRequest.Request.Item;
|
||||
var shellSection = navigationRequest.Request.Section;
|
||||
|
@ -555,6 +528,8 @@ namespace Xamarin.Forms
|
|||
ShellContent shellContent = navigationRequest.Request.Content;
|
||||
bool modalStackPreBuilt = false;
|
||||
|
||||
|
||||
|
||||
// If we're replacing the whole stack and there are global routes then build the navigation stack before setting the shell section visible
|
||||
if (navigationRequest.Request.GlobalRoutes.Count > 0 &&
|
||||
nextActiveSection != null &&
|
||||
|
@ -563,17 +538,17 @@ namespace Xamarin.Forms
|
|||
modalStackPreBuilt = true;
|
||||
|
||||
bool? isAnimated = (nextActiveSection != currentShellSection) ? false : animate;
|
||||
await nextActiveSection.GoToAsync(navigationRequest, queryData, isAnimated);
|
||||
await nextActiveSection.GoToAsync(navigationRequest, queryData, isAnimated, isRelativePopping);
|
||||
}
|
||||
|
||||
if (shellItem != null)
|
||||
{
|
||||
ApplyQueryAttributes(shellItem, queryData, navigationRequest.Request.Section == null);
|
||||
ApplyQueryAttributes(shellItem, queryData, navigationRequest.Request.Section == null, false);
|
||||
bool navigatedToNewShellElement = false;
|
||||
|
||||
if (shellSection != null && shellContent != null)
|
||||
{
|
||||
Shell.ApplyQueryAttributes(shellContent, queryData, navigationRequest.Request.GlobalRoutes.Count == 0);
|
||||
ApplyQueryAttributes(shellContent, queryData, navigationRequest.Request.GlobalRoutes.Count == 0, isRelativePopping);
|
||||
if (shellSection.CurrentItem != shellContent)
|
||||
{
|
||||
shellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, shellContent);
|
||||
|
@ -583,7 +558,7 @@ namespace Xamarin.Forms
|
|||
|
||||
if (shellSection != null)
|
||||
{
|
||||
Shell.ApplyQueryAttributes(shellSection, queryData, navigationRequest.Request.Content == null);
|
||||
Shell.ApplyQueryAttributes(shellSection, queryData, navigationRequest.Request.Content == null, false);
|
||||
if (shellItem.CurrentItem != shellSection)
|
||||
{
|
||||
shellItem.SetValueFromRenderer(ShellItem.CurrentItemProperty, shellSection);
|
||||
|
@ -620,7 +595,7 @@ namespace Xamarin.Forms
|
|||
// TODO get rid of this hack and fix so if there's a stack the current page doesn't display
|
||||
await Device.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
return CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate);
|
||||
return CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate, isRelativePopping);
|
||||
});
|
||||
}
|
||||
else if (navigationRequest.Request.GlobalRoutes.Count == 0 &&
|
||||
|
@ -630,13 +605,13 @@ namespace Xamarin.Forms
|
|||
// TODO get rid of this hack and fix so if there's a stack the current page doesn't display
|
||||
await Device.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
return CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate);
|
||||
return CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate, isRelativePopping);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate);
|
||||
await CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate, isRelativePopping);
|
||||
}
|
||||
|
||||
_accumulateNavigatedEvents = false;
|
||||
|
@ -646,7 +621,7 @@ namespace Xamarin.Forms
|
|||
HandleNavigated(_accumulatedEvent);
|
||||
}
|
||||
|
||||
internal static void ApplyQueryAttributes(Element element, IDictionary<string, string> query, bool isLastItem)
|
||||
internal static void ApplyQueryAttributes(Element element, IDictionary<string, string> query, bool isLastItem, bool isPopping)
|
||||
{
|
||||
string prefix = "";
|
||||
if (!isLastItem)
|
||||
|
@ -673,6 +648,7 @@ namespace Xamarin.Forms
|
|||
|
||||
//filter the query to only apply the keys with matching prefix
|
||||
var filteredQuery = new Dictionary<string, string>(query.Count);
|
||||
|
||||
foreach (var q in query)
|
||||
{
|
||||
if (!q.Key.StartsWith(prefix, StringComparison.Ordinal))
|
||||
|
@ -683,10 +659,32 @@ namespace Xamarin.Forms
|
|||
filteredQuery.Add(key, q.Value);
|
||||
}
|
||||
|
||||
|
||||
if (baseShellItem is ShellContent)
|
||||
baseShellItem.ApplyQueryAttributes(filteredQuery);
|
||||
baseShellItem.ApplyQueryAttributes(MergeData(element, filteredQuery, isPopping));
|
||||
else if (isLastItem)
|
||||
element.SetValue(ShellContent.QueryAttributesProperty, query);
|
||||
element.SetValue(ShellContent.QueryAttributesProperty, MergeData(element, query, isPopping));
|
||||
|
||||
IDictionary<string,string> MergeData(Element shellElement, IDictionary<string, string> data, bool isPopping)
|
||||
{
|
||||
if (!isPopping)
|
||||
return data;
|
||||
|
||||
var returnValue = new Dictionary<string, string>(data);
|
||||
|
||||
var existing = (IDictionary<string, string>)shellElement.GetValue(ShellContent.QueryAttributesProperty);
|
||||
|
||||
if (existing == null)
|
||||
return data;
|
||||
|
||||
foreach(var datum in existing)
|
||||
{
|
||||
if (!returnValue.ContainsKey(datum.Key))
|
||||
returnValue[datum.Key] = datum.Value;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
internal static ShellNavigationState GetNavigationState(ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList<Page> sectionStack, IReadOnlyList<Page> modalStack)
|
||||
|
@ -1286,9 +1284,13 @@ namespace Xamarin.Forms
|
|||
internal void HandleNavigated(ShellNavigatedEventArgs args)
|
||||
{
|
||||
if (_accumulateNavigatedEvents)
|
||||
_accumulatedEvent = args;
|
||||
{
|
||||
if(_accumulatedEvent == null)
|
||||
_accumulatedEvent = args;
|
||||
}
|
||||
else
|
||||
{
|
||||
_accumulatedEvent = null;
|
||||
BaseShellItem baseShellItem = CurrentItem?.CurrentItem?.CurrentItem;
|
||||
|
||||
if (baseShellItem != null)
|
||||
|
@ -1653,6 +1655,79 @@ namespace Xamarin.Forms
|
|||
return !navArgs.Cancelled && navArgs.DeferralCount == 0;
|
||||
}
|
||||
|
||||
ShellNavigationSource CalculateNavigationSource(ShellNavigationState current, NavigationRequest request)
|
||||
{
|
||||
if (request.StackRequest == NavigationRequest.WhatToDoWithTheStack.PushToIt)
|
||||
return ShellNavigationSource.Push;
|
||||
|
||||
if (current == null)
|
||||
return ShellNavigationSource.ShellItemChanged;
|
||||
|
||||
var targetUri = ShellUriHandler.ConvertToStandardFormat(this, request.Request.FullUri);
|
||||
var currentUri = ShellUriHandler.ConvertToStandardFormat(this, current.FullLocation);
|
||||
|
||||
var targetPaths = ShellUriHandler.RetrievePaths(targetUri.PathAndQuery);
|
||||
var currentPaths = ShellUriHandler.RetrievePaths(currentUri.PathAndQuery);
|
||||
|
||||
var targetPathsLength = targetPaths.Length;
|
||||
var currentPathsLength = currentPaths.Length;
|
||||
|
||||
if (targetPathsLength < 4 || currentPathsLength < 4)
|
||||
return ShellNavigationSource.Unknown;
|
||||
|
||||
if (targetPaths[1] != currentPaths[1])
|
||||
return ShellNavigationSource.ShellItemChanged;
|
||||
if (targetPaths[2] != currentPaths[2])
|
||||
return ShellNavigationSource.ShellSectionChanged;
|
||||
if (targetPaths[3] != currentPaths[3])
|
||||
return ShellNavigationSource.ShellContentChanged;
|
||||
|
||||
if (targetPathsLength == currentPathsLength)
|
||||
return ShellNavigationSource.Unknown;
|
||||
|
||||
if(targetPathsLength < currentPathsLength)
|
||||
{
|
||||
for (var i = 0; i < targetPathsLength; i++)
|
||||
{
|
||||
var targetPath = targetPaths[i];
|
||||
if (targetPath != currentPaths[i])
|
||||
break;
|
||||
|
||||
if (i == targetPathsLength - 1)
|
||||
{
|
||||
if (targetPathsLength == 4)
|
||||
return ShellNavigationSource.PopToRoot;
|
||||
|
||||
return ShellNavigationSource.Pop;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetPaths[targetPathsLength - 1] == currentPaths[currentPathsLength - 1])
|
||||
return ShellNavigationSource.Remove;
|
||||
|
||||
if (targetPathsLength == 4)
|
||||
return ShellNavigationSource.PopToRoot;
|
||||
|
||||
return ShellNavigationSource.Pop;
|
||||
}
|
||||
else if(targetPathsLength > currentPathsLength)
|
||||
{
|
||||
for (var i = 0; i < currentPathsLength; i++)
|
||||
{
|
||||
if (targetPaths[i] != currentPaths[i])
|
||||
break;
|
||||
|
||||
if (i == targetPathsLength - 1)
|
||||
return ShellNavigationSource.Push;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetPaths[targetPathsLength - 1] == currentPaths[currentPathsLength - 1])
|
||||
return ShellNavigationSource.Insert;
|
||||
|
||||
return ShellNavigationSource.Push;
|
||||
}
|
||||
|
||||
internal Element GetVisiblePage()
|
||||
{
|
||||
if (CurrentItem?.CurrentItem is IShellSectionController scc)
|
||||
|
|
|
@ -61,21 +61,6 @@ namespace Xamarin.Forms
|
|||
|
||||
IShellItemController ShellItemController => this;
|
||||
|
||||
internal Task GoToPart(NavigationRequest request, Dictionary<string, string> queryData)
|
||||
{
|
||||
var shellSection = request.Request.Section;
|
||||
|
||||
if (shellSection == null)
|
||||
shellSection = ShellItemController.GetItems()[0];
|
||||
|
||||
Shell.ApplyQueryAttributes(shellSection, queryData, request.Request.Content == null);
|
||||
|
||||
if (CurrentItem != shellSection)
|
||||
SetValueFromRenderer(CurrentItemProperty, shellSection);
|
||||
|
||||
return shellSection.GoToPart(request, queryData);
|
||||
}
|
||||
|
||||
bool IShellItemController.ProposeSection(ShellSection shellSection, bool setValue)
|
||||
{
|
||||
var controller = (IShellController)Parent;
|
||||
|
|
|
@ -76,30 +76,6 @@ namespace Xamarin.Forms
|
|||
callback(DisplayedPage);
|
||||
}
|
||||
|
||||
internal Task GoToPart(NavigationRequest request, Dictionary<string, string> queryData)
|
||||
{
|
||||
ShellContent shellContent = request.Request.Content;
|
||||
|
||||
if (shellContent == null)
|
||||
shellContent = ShellSectionController.GetItems()[0];
|
||||
|
||||
if (request.Request.GlobalRoutes.Count > 0)
|
||||
{
|
||||
// TODO get rid of this hack and fix so if there's a stack the current page doesn't display
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await GoToAsync(request, queryData, false);
|
||||
});
|
||||
}
|
||||
|
||||
Shell.ApplyQueryAttributes(shellContent, queryData, request.Request.GlobalRoutes.Count == 0);
|
||||
|
||||
if (CurrentItem != shellContent)
|
||||
SetValueFromRenderer(CurrentItemProperty, shellContent);
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
bool IShellSectionController.RemoveContentInsetObserver(IShellContentInsetObserver observer)
|
||||
{
|
||||
return _observers.Remove(observer);
|
||||
|
@ -332,7 +308,7 @@ namespace Xamarin.Forms
|
|||
return (ShellSection)(ShellContent)page;
|
||||
}
|
||||
|
||||
async Task PrepareCurrentStackForBeingReplaced(NavigationRequest request, IDictionary<string, string> queryData, bool? animate, List<string> globalRoutes)
|
||||
async Task PrepareCurrentStackForBeingReplaced(NavigationRequest request, IDictionary<string, string> queryData, bool? animate, List<string> globalRoutes, bool isRelativePopping)
|
||||
{
|
||||
string route = "";
|
||||
List<Page> navStack = null;
|
||||
|
@ -368,6 +344,7 @@ namespace Xamarin.Forms
|
|||
List<Page> pagesToInsert = new List<Page>();
|
||||
for (int i = 0; i < globalRoutes.Count; i++)
|
||||
{
|
||||
bool isLast = i == globalRoutes.Count - 1;
|
||||
int navIndex = i + 1;
|
||||
// Routes match so don't do anything
|
||||
if (navIndex < _navStack.Count && Routing.GetRoute(_navStack[navIndex]) == globalRoutes[i])
|
||||
|
@ -375,12 +352,16 @@ namespace Xamarin.Forms
|
|||
continue;
|
||||
}
|
||||
|
||||
var page = GetOrCreateFromRoute(globalRoutes[i], queryData, i == globalRoutes.Count - 1);
|
||||
var page = GetOrCreateFromRoute(globalRoutes[i], queryData, i == globalRoutes.Count - 1, false);
|
||||
if (IsModal(page))
|
||||
{
|
||||
await Navigation.PushModalAsync(page, IsNavigationAnimated(page));
|
||||
break;
|
||||
}
|
||||
else if(!isLast && navIndex < _navStack.Count)
|
||||
{
|
||||
Navigation.InsertPageBefore(page, _navStack[navIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
pagesToInsert.Add(page);
|
||||
|
@ -414,7 +395,7 @@ namespace Xamarin.Forms
|
|||
// if the routes do match and this is the last in the loop
|
||||
// pop everything after this route
|
||||
popCount = i + 2;
|
||||
Shell.ApplyQueryAttributes(navPage, queryData, isLast);
|
||||
Shell.ApplyQueryAttributes(navPage, queryData, isLast, isRelativePopping);
|
||||
|
||||
// If we're not on the last loop of the stack then continue
|
||||
// otherwise pop the rest of the stack
|
||||
|
@ -507,7 +488,7 @@ namespace Xamarin.Forms
|
|||
return startingList;
|
||||
}
|
||||
|
||||
Page GetOrCreateFromRoute(string route, IDictionary<string, string> queryData, bool isLast)
|
||||
Page GetOrCreateFromRoute(string route, IDictionary<string, string> queryData, bool isLast, bool isPopping)
|
||||
{
|
||||
var content = Routing.GetOrCreateContent(route) as Page;
|
||||
if (content == null)
|
||||
|
@ -515,15 +496,13 @@ namespace Xamarin.Forms
|
|||
Internals.Log.Warning(nameof(Shell), $"Failed to Create Content For: {route}");
|
||||
}
|
||||
|
||||
Shell.ApplyQueryAttributes(content, queryData, isLast);
|
||||
Shell.ApplyQueryAttributes(content, queryData, isLast, isPopping);
|
||||
return content;
|
||||
}
|
||||
|
||||
internal async Task GoToAsync(NavigationRequest request, IDictionary<string, string> queryData, bool? animate)
|
||||
internal async Task GoToAsync(NavigationRequest request, IDictionary<string, string> queryData, bool? animate, bool isRelativePopping)
|
||||
{
|
||||
List<string> globalRoutes = request.Request.GlobalRoutes;
|
||||
string route = String.Empty;
|
||||
|
||||
if (globalRoutes == null || globalRoutes.Count == 0)
|
||||
{
|
||||
if (_navStack.Count == 2)
|
||||
|
@ -534,7 +513,7 @@ namespace Xamarin.Forms
|
|||
return;
|
||||
}
|
||||
|
||||
await PrepareCurrentStackForBeingReplaced(request, queryData, animate, globalRoutes);
|
||||
await PrepareCurrentStackForBeingReplaced(request, queryData, animate, globalRoutes, isRelativePopping);
|
||||
|
||||
List<Page> modalPageStacks = new List<Page>();
|
||||
List<Page> nonModalPageStacks = new List<Page>();
|
||||
|
@ -552,8 +531,7 @@ namespace Xamarin.Forms
|
|||
for (int i = whereToStartNavigation; i < globalRoutes.Count; i++)
|
||||
{
|
||||
bool isLast = i == globalRoutes.Count - 1;
|
||||
route = globalRoutes[i];
|
||||
var content = GetOrCreateFromRoute(route, queryData, isLast);
|
||||
var content = GetOrCreateFromRoute(globalRoutes[i], queryData, isLast, false);
|
||||
if (content == null)
|
||||
{
|
||||
break;
|
||||
|
@ -999,7 +977,7 @@ namespace Xamarin.Forms
|
|||
|
||||
if (shellSection.Parent?.Parent is IShellController shell && shellSection.IsVisibleSection)
|
||||
{
|
||||
shell.UpdateCurrentState(ShellNavigationSource.ShellSectionChanged);
|
||||
shell.UpdateCurrentState(ShellNavigationSource.ShellContentChanged);
|
||||
}
|
||||
|
||||
shellSection.SendStructureChanged();
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using IOPath = System.IO.Path;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
|
@ -51,6 +52,33 @@ namespace Xamarin.Forms
|
|||
return new Uri(path, UriKind.Relative);
|
||||
}
|
||||
|
||||
public static bool IsTargetRelativePop(ShellNavigationParameters request)
|
||||
{
|
||||
if (request?.TargetState?.Location?.OriginalString == null)
|
||||
return false;
|
||||
|
||||
bool isRelativePopping = false;
|
||||
|
||||
// If the user is popping with PopAsync or ".."
|
||||
// we need to know this so we don't clear the query parameters off
|
||||
// the destination location
|
||||
|
||||
var dest = request.TargetState?.Location?.OriginalString;
|
||||
|
||||
foreach (var path in RetrievePaths(dest))
|
||||
{
|
||||
if (path != "..")
|
||||
{
|
||||
isRelativePopping = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
isRelativePopping = true;
|
||||
}
|
||||
|
||||
return isRelativePopping;
|
||||
}
|
||||
|
||||
public static Uri ConvertToStandardFormat(Shell shell, Uri request)
|
||||
{
|
||||
request = FormatUri(request, shell);
|
||||
|
@ -69,7 +97,7 @@ namespace Xamarin.Forms
|
|||
if (pathAndQuery.Length > 1)
|
||||
query = $"?{pathAndQuery[1]}";
|
||||
|
||||
var segments = new List<string>(pathAndQuery[0].Split(_pathSeparators, StringSplitOptions.RemoveEmptyEntries));
|
||||
var segments = new List<string>(RetrievePaths(pathAndQuery[0]));
|
||||
|
||||
if (segments[0] != routeHost)
|
||||
segments.Insert(0, routeHost);
|
||||
|
@ -83,18 +111,25 @@ namespace Xamarin.Forms
|
|||
return new Uri(uri);
|
||||
}
|
||||
|
||||
static internal string[] RetrievePaths(string uri) => uri.Split(_pathSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
static internal NavigationRequest.WhatToDoWithTheStack CalculateStackRequest(Uri uri)
|
||||
{
|
||||
if (uri.IsAbsoluteUri)
|
||||
return NavigationRequest.WhatToDoWithTheStack.ReplaceIt;
|
||||
else if (uri.OriginalString.StartsWith("//", StringComparison.Ordinal) || uri.OriginalString.StartsWith("\\\\", StringComparison.Ordinal))
|
||||
return NavigationRequest.WhatToDoWithTheStack.ReplaceIt;
|
||||
|
||||
|
||||
return NavigationRequest.WhatToDoWithTheStack.PushToIt;
|
||||
}
|
||||
|
||||
internal static NavigationRequest GetNavigationRequest(Shell shell, Uri uri, bool enableRelativeShellRoutes = false, bool throwNavigationErrorAsException = true, ShellNavigationParameters shellNavigationParameters = null)
|
||||
{
|
||||
uri = FormatUri(uri, shell);
|
||||
|
||||
// figure out the intent of the Uri
|
||||
NavigationRequest.WhatToDoWithTheStack whatDoIDo = NavigationRequest.WhatToDoWithTheStack.PushToIt;
|
||||
if (uri.IsAbsoluteUri)
|
||||
whatDoIDo = NavigationRequest.WhatToDoWithTheStack.ReplaceIt;
|
||||
else if (uri.OriginalString.StartsWith("//", StringComparison.Ordinal) || uri.OriginalString.StartsWith("\\\\", StringComparison.Ordinal))
|
||||
whatDoIDo = NavigationRequest.WhatToDoWithTheStack.ReplaceIt;
|
||||
else
|
||||
whatDoIDo = NavigationRequest.WhatToDoWithTheStack.PushToIt;
|
||||
NavigationRequest.WhatToDoWithTheStack whatDoIDo = CalculateStackRequest(uri);
|
||||
|
||||
Uri request = ConvertToStandardFormat(shell, uri);
|
||||
|
||||
|
@ -128,13 +163,9 @@ namespace Xamarin.Forms
|
|||
}
|
||||
|
||||
var theWinningRoute = possibleRouteMatches[0];
|
||||
|
||||
RequestDefinition definition =
|
||||
new RequestDefinition(
|
||||
ConvertToStandardFormat(shell, CreateUri(theWinningRoute.PathFull)),
|
||||
theWinningRoute.Item,
|
||||
theWinningRoute.Section,
|
||||
theWinningRoute.Content,
|
||||
theWinningRoute.GlobalRouteMatches);
|
||||
new RequestDefinition(theWinningRoute, shell);
|
||||
|
||||
NavigationRequest navigationRequest = new NavigationRequest(definition, whatDoIDo, request.Query, request.Fragment);
|
||||
|
||||
|
@ -174,7 +205,7 @@ namespace Xamarin.Forms
|
|||
!originalRequest.OriginalString.StartsWith("//", StringComparison.Ordinal))
|
||||
relativeMatch = true;
|
||||
|
||||
var segments = localPath.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
var segments = RetrievePaths(localPath);
|
||||
|
||||
if (!relativeMatch)
|
||||
{
|
||||
|
@ -566,7 +597,7 @@ namespace Xamarin.Forms
|
|||
if (key.StartsWith(_pathSeparator, StringComparison.Ordinal) && !(node is Shell))
|
||||
continue;
|
||||
|
||||
var segments = key.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
var segments = RetrievePaths(key);
|
||||
|
||||
if (segments[0] == route)
|
||||
{
|
||||
|
@ -589,7 +620,7 @@ namespace Xamarin.Forms
|
|||
{
|
||||
get
|
||||
{
|
||||
var segments = _path.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList().Skip(1).ToList();
|
||||
var segments = RetrievePaths(_path).ToList().Skip(1).ToList();
|
||||
|
||||
if (segments.Count == 0)
|
||||
return new object[0];
|
||||
|
@ -604,7 +635,7 @@ namespace Xamarin.Forms
|
|||
{
|
||||
get
|
||||
{
|
||||
var segments = _path.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
var segments = RetrievePaths(_path);
|
||||
|
||||
if (segments.Length == 0)
|
||||
return string.Empty;
|
||||
|
@ -617,7 +648,7 @@ namespace Xamarin.Forms
|
|||
{
|
||||
get
|
||||
{
|
||||
var segments = _path.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList().Skip(1).ToList();
|
||||
var segments = RetrievePaths(_path).ToList().Skip(1).ToList();
|
||||
|
||||
if (segments.Count == 0)
|
||||
return true;
|
||||
|
@ -717,7 +748,6 @@ namespace Xamarin.Forms
|
|||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// if shellSegment == userSegment it means the implicit route is part of the request
|
||||
|
@ -750,6 +780,7 @@ namespace Xamarin.Forms
|
|||
return Routing.FormatRoute(String.Join(_uriSeparator, _allSegments.Skip(nextMatch)));
|
||||
}
|
||||
}
|
||||
|
||||
public string[] RemainingSegments
|
||||
{
|
||||
get
|
||||
|
@ -776,7 +807,6 @@ namespace Xamarin.Forms
|
|||
public bool IsFullMatch => _matchedSegments.Count == _allSegments.Length;
|
||||
public List<string> GlobalRouteMatches => _globalRouteMatches;
|
||||
public List<string> SegmentsMatched => _matchedSegments;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -808,18 +838,38 @@ namespace Xamarin.Forms
|
|||
[DebuggerDisplay("Full = {FullUri}, Short = {ShortUri}")]
|
||||
internal class RequestDefinition
|
||||
{
|
||||
public RequestDefinition(Uri fullUri, ShellItem item, ShellSection section, ShellContent content, List<string> globalRoutes)
|
||||
public RequestDefinition(RouteRequestBuilder theWinningRoute, Shell shell)
|
||||
{
|
||||
FullUri = fullUri;
|
||||
Item = item;
|
||||
Section = section;
|
||||
Content = content;
|
||||
GlobalRoutes = globalRoutes;
|
||||
Item = theWinningRoute.Item;
|
||||
Section = theWinningRoute.Section ?? Item?.CurrentItem;
|
||||
Content = theWinningRoute.Content ?? Section?.CurrentItem;
|
||||
GlobalRoutes = theWinningRoute.GlobalRouteMatches;
|
||||
|
||||
List<String> builder = new List<string>();
|
||||
if (Item?.Route != null)
|
||||
builder.Add(Item.Route);
|
||||
|
||||
if (Section?.Route != null)
|
||||
builder.Add(Section?.Route);
|
||||
|
||||
if (Content?.Route != null)
|
||||
builder.Add(Content?.Route);
|
||||
|
||||
if (GlobalRoutes != null)
|
||||
builder.AddRange(GlobalRoutes);
|
||||
|
||||
var uriPath = MakeUriString(builder);
|
||||
var uri = ShellUriHandler.CreateUri(uriPath);
|
||||
FullUri = ShellUriHandler.ConvertToStandardFormat(shell, uri);
|
||||
|
||||
}
|
||||
|
||||
public RequestDefinition(string fullUri, ShellItem item, ShellSection section, ShellContent content, List<string> globalRoutes) :
|
||||
this(new Uri(fullUri, UriKind.Absolute), item, section, content, globalRoutes)
|
||||
string MakeUriString(List<string> segments)
|
||||
{
|
||||
if (segments[0].StartsWith("/", StringComparison.Ordinal) || segments[0].StartsWith("\\", StringComparison.Ordinal))
|
||||
return String.Join("/", segments);
|
||||
|
||||
return $"//{String.Join("/", segments)}";
|
||||
}
|
||||
|
||||
public Uri FullUri { get; }
|
||||
|
|
Загрузка…
Ссылка в новой задаче