Skip to content

Commit

Permalink
Merge pull request #3110 from PrismLibrary/dev/ds/is-rootpage
Browse files Browse the repository at this point in the history
Fixing IsRootPage
  • Loading branch information
dansiegel authored Mar 27, 2024
2 parents 2a25770 + d06685a commit e4e6edb
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 89 deletions.
213 changes: 128 additions & 85 deletions src/Maui/Prism.Maui/Navigation/PrismWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,112 +6,155 @@
using Prism.Navigation.Xaml;
using TabbedPage = Microsoft.Maui.Controls.TabbedPage;

namespace Prism.Navigation;

internal class PrismWindow : Window
namespace Prism.Navigation
{
public const string DefaultWindowName = "__PrismRootWindow";

public PrismWindow(string name = DefaultWindowName)
/// <summary>
/// Represents a window used for Prism navigation in a Maui application.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public class PrismWindow : Window
{
Name = name;
ModalPopping += PrismWindow_ModalPopping;
}
/// <summary>
/// The default name for the Prism window.
/// </summary>
public const string DefaultWindowName = "__PrismRootWindow";

/// <summary>
/// Initializes a new instance of the <see cref="PrismWindow"/> class with the specified name.
/// </summary>
/// <param name="name">The name of the window.</param>
public PrismWindow(string name = DefaultWindowName)
{
Name = name;
ModalPopping += PrismWindow_ModalPopping;
}

public string Name { get; }
/// <summary>
/// Gets the name of the window.
/// </summary>
public string Name { get; }

public bool IsActive { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether the window is active.
/// </summary>
public bool IsActive { get; internal set; }

internal Page CurrentPage => Page is null ? null : MvvmHelpers.GetCurrentPage(Page);
/// <summary>
/// Gets the current page displayed in the window.
/// </summary>
internal Page CurrentPage => Page is null ? null : MvvmHelpers.GetCurrentPage(Page);

internal bool IsRootPage => Page switch
{
TabbedPage tabbed => tabbed.CurrentPage,
NavigationPage nav => nav.RootPage,
_ => Page
} == CurrentPage;
/// <summary>
/// Gets a value indicating whether the current page is the root page.
/// </summary>
internal bool IsRootPage => GetRootPage(Page) == CurrentPage;

[EditorBrowsable(EditorBrowsableState.Never)]
public void OnSystemBack()
{
var currentPage = CurrentPage;
if(currentPage?.Parent is NavigationPage navPage)
private Page GetRootPage(Page page) =>
page switch
{
TabbedPage tabbed => GetRootPage(tabbed.CurrentPage),
NavigationPage nav => GetRootPage(nav.RootPage),
FlyoutPage flyout => GetRootPage(flyout.Detail),
_ => page
};

/// <summary>
/// Handles the system back button press.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public void OnSystemBack()
{
// The NavigationPage has already taken care of the GoBack
return;
}
var currentPage = CurrentPage;
if (currentPage?.Parent is NavigationPage navPage)
{
// The NavigationPage has already taken care of the GoBack
return;
}

var container = currentPage.GetContainerProvider();
var container = currentPage.GetContainerProvider();

if (IsRoot(currentPage))
{
var app = container.Resolve<IApplication>() as Application;
app.Quit();
return;
}
else if (currentPage is IDialogContainer dialogContainer)
{
if (dialogContainer.Dismiss.CanExecute(null))
dialogContainer.Dismiss.Execute(null);
}
else
{
var navigation = container.Resolve<INavigationService>();
navigation.GoBackAsync();
if (IsRoot(currentPage))
{
var app = container.Resolve<IApplication>() as Application;
app.Quit();
return;
}
else if (currentPage is IDialogContainer dialogContainer)
{
if (dialogContainer.Dismiss.CanExecute(null))
dialogContainer.Dismiss.Execute(null);
}
else
{
var navigation = container.Resolve<INavigationService>();
navigation.GoBackAsync();
}
}
}

private bool IsRoot(Page page)
{
if (page == Page) return true;

return page.Parent switch
private bool IsRoot(Page page)
{
FlyoutPage flyout => IsRoot(flyout),
TabbedPage tabbed => IsRoot(tabbed),
NavigationPage navigation => IsRoot(navigation),
_ => false
};
}
if (page == Page) return true;

private async void PrismWindow_ModalPopping(object sender, ModalPoppingEventArgs e)
{
if (PageNavigationService.NavigationSource == PageNavigationSource.Device)
{
e.Cancel = true;
var dialogModal = IDialogContainer.DialogStack.LastOrDefault();
if (dialogModal is not null)
return page.Parent switch
{
if (dialogModal.Dismiss.CanExecute(null))
dialogModal.Dismiss.Execute(null);
}
else
FlyoutPage flyout => IsRoot(flyout),
TabbedPage tabbed => IsRoot(tabbed),
NavigationPage navigation => IsRoot(navigation),
_ => false
};
}

private async void PrismWindow_ModalPopping(object sender, ModalPoppingEventArgs e)
{
if (PageNavigationService.NavigationSource == PageNavigationSource.Device)
{
var navService = Xaml.Navigation.GetNavigationService(e.Modal);
await navService.GoBackAsync();
e.Cancel = true;
var dialogModal = IDialogContainer.DialogStack.LastOrDefault();
if (dialogModal is not null)
{
if (dialogModal.Dismiss.CanExecute(null))
dialogModal.Dismiss.Execute(null);
}
else
{
var navService = Xaml.Navigation.GetNavigationService(e.Modal);
await navService.GoBackAsync();
}
}
}
}

protected override void OnActivated()
{
IsActive = true;
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(CurrentPage, x => x.IsActive = true);
}
/// <summary>
/// Called when the window is activated.
/// </summary>
protected override void OnActivated()
{
IsActive = true;
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(CurrentPage, x => x.IsActive = true);
}

protected override void OnDeactivated()
{
IsActive = false;
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(CurrentPage, x => x.IsActive = true);
}
/// <summary>
/// Called when the window is deactivated.
/// </summary>
protected override void OnDeactivated()
{
IsActive = false;
MvvmHelpers.InvokeViewAndViewModelAction<IActiveAware>(CurrentPage, x => x.IsActive = true);
}

protected override void OnResumed()
{
MvvmHelpers.InvokeViewAndViewModelAction<IApplicationLifecycleAware>(CurrentPage, x => x.OnResume());
}
/// <summary>
/// Called when the window is resumed.
/// </summary>
protected override void OnResumed()
{
MvvmHelpers.InvokeViewAndViewModelAction<IApplicationLifecycleAware>(CurrentPage, x => x.OnResume());
}

protected override void OnStopped()
{
MvvmHelpers.InvokeViewAndViewModelAction<IApplicationLifecycleAware>(CurrentPage, x => x.OnSleep());
/// <summary>
/// Called when the window is stopped.
/// </summary>
protected override void OnStopped()
{
MvvmHelpers.InvokeViewAndViewModelAction<IApplicationLifecycleAware>(CurrentPage, x => x.OnSleep());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using Moq;
using NuGet.Frameworks;
using Prism.Ioc;
using Prism.Navigation;
using Prism.Navigation.Regions;
using Prism.Navigation.Regions.Navigation;
using Prism.Navigation.Regions.Navigation;
using Xamarin.Forms;
using Xunit;
using Region = Prism.Navigation.Regions.Region;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Prism.Controls;
using Prism.DryIoc.Maui.Tests.Mocks.Views;

namespace Prism.DryIoc.Maui.Tests.Fixtures.Navigation;

public class PrismWindowTests : TestBase
{
public PrismWindowTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
}

[Fact]
public void CurrentPageEqualsRootPage()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow("MockViewA"))
.Build();
var window = GetWindow(mauiApp);

Assert.IsType<MockViewA>(window.CurrentPage);
Assert.IsType<MockViewA>(window.Page);
Assert.True(window.IsRootPage);
}

[Fact]
public void CurrentPage_FromNavigationPage_EqualsRootPage()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow("NavigationPage/MockViewA"))
.Build();
var window = GetWindow(mauiApp);

Assert.IsType<MockViewA>(window.CurrentPage);
Assert.IsType<PrismNavigationPage>(window.Page);
Assert.True(window.IsRootPage);
}

[Fact]
public void CurrentPage_FromNavigationPage_IsNotRootPage()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow("NavigationPage/MockViewA/MockViewB"))
.Build();
var window = GetWindow(mauiApp);

Assert.IsType<MockViewB>(window.CurrentPage);
Assert.IsType<PrismNavigationPage>(window.Page);
Assert.False(window.IsRootPage);
}

[Fact]
public void CurrentPage_IsRoot_FromTabbedPage_WithNavigationPageTab()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow(n =>
n.CreateBuilder()
.AddTabbedSegment(b => b.CreateTab(t => t.AddNavigationPage().AddSegment("MockViewA"))
.CreateTab("MockViewB")).NavigateAsync()))
.Build();
var window = GetWindow(mauiApp);

Assert.IsType<MockViewA>(window.CurrentPage);
Assert.IsType<TabbedPage>(window.Page);
Assert.True(window.IsRootPage);
}

[Fact]
public void CurrentPage_IsNotRoot_FromTabbedPage_WithDeepLinkedNavigationPageTab()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow(n =>
n.CreateBuilder()
.AddTabbedSegment(b => b.CreateTab(t =>
t.AddNavigationPage()
.AddSegment("MockViewA")
.AddSegment("MockViewC"))
.CreateTab("MockViewB")).NavigateAsync()))
.Build();
var window = GetWindow(mauiApp);

Assert.IsType<MockViewC>(window.CurrentPage);
Assert.IsType<TabbedPage>(window.Page);
Assert.False(window.IsRootPage);
}

[Fact]
public void CurrentPage_IsRoot_WithFlyoutPage()
{
var mauiApp = CreateBuilder(prism => prism.CreateWindow(n =>
n.CreateBuilder()
.AddSegment("MockHome")
.AddNavigationPage()
.AddSegment("MockViewA")
.NavigateAsync()))
.Build();

var window = GetWindow(mauiApp);

Assert.IsType<MockViewA>(window.CurrentPage);
Assert.IsType<MockHome>(window.Page);
Assert.True(window.IsRootPage);
}
}
2 changes: 1 addition & 1 deletion tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ protected MauiAppBuilder CreateBuilder(Action<PrismAppBuilder> configurePrism)
});
}

protected static Window GetWindow(MauiApp mauiApp)
protected static PrismWindow GetWindow(MauiApp mauiApp)
{
var app = mauiApp.Services.GetService<IApplication>();
Assert.NotNull(app);
Expand Down
2 changes: 1 addition & 1 deletion tests/Prism.Core.Tests/Prism.Core.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net6.0</TargetFrameworks>
<TargetFrameworks>net6.0</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand Down

0 comments on commit e4e6edb

Please sign in to comment.