Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Controls/src/Core/HandlerImpl/Window/Window.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ void SendWindowAppearing()
{
if (Navigation.ModalStack.Count == 0)
Page?.SendAppearing();
else if (Page is Shell shell)
shell.SendAppearing();
}

void SendWindowDisppearing()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,17 @@ public async Task PushModalAsync(Page modal, bool animated)
{
if (ModalStack.Count == 0)
{
modal.NavigationProxy.Inner = _window.Navigation;
if (modal.NavigationProxy.Inner is null)
modal.NavigationProxy.Inner = _window.Navigation;

await PushModalPlatformAsync(modal, animated);
}
else
{
await PushModalPlatformAsync(modal, animated);
modal.NavigationProxy.Inner = _window.Navigation;

if (modal.NavigationProxy.Inner is null)
modal.NavigationProxy.Inner = _window.Navigation;
}
}

Expand Down
16 changes: 15 additions & 1 deletion src/Controls/src/Core/Shell/NavigableElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,21 @@ protected override void OnParentSet()

if (navProxy != null)
{
NavigationProxy.Inner = navProxy.NavigationProxy;
// Modal navigation needs to all proxy through Shell
// So, when the modal page is parented to the window
// we make sure to set the NavProxy on the modal page to the
// Shell wrapper
if (Parent is Window w &&
w.Page is Shell shell &&
this is not Shell)
{
if (NavigationProxy.Inner is not Shell.ShellNavigationImplWrapper)
NavigationProxy.Inner = new Shell.ShellNavigationImplWrapper(navProxy.NavigationProxy, shell.Navigation);
}
else
{
NavigationProxy.Inner = navProxy.NavigationProxy;
}
}
else
{
Expand Down
28 changes: 16 additions & 12 deletions src/Controls/src/Core/Shell/Shell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1710,24 +1710,28 @@ protected override async Task OnPushModal(Page modal, bool animated)

await base.OnPushModal(modal, animated);

modal.NavigationProxy.Inner = new NavigationImplWrapper(modal.NavigationProxy.Inner, this);
if (modal.NavigationProxy.Inner is not ShellNavigationImplWrapper &&
modal.NavigationProxy.Inner is not null)
{
modal.NavigationProxy.Inner = new ShellNavigationImplWrapper(modal.NavigationProxy.Inner, this);
}
}
}

class NavigationImplWrapper : NavigationProxy
{
readonly INavigation _shellProxy;
internal class ShellNavigationImplWrapper : NavigationProxy
{
readonly INavigation _shellProxy;

public NavigationImplWrapper(INavigation proxy, INavigation shellProxy)
{
Inner = proxy;
_shellProxy = shellProxy;
public ShellNavigationImplWrapper(INavigation proxy, INavigation shellProxy)
{
Inner = proxy;
_shellProxy = shellProxy;

}
}

protected override Task<Page> OnPopModal(bool animated) => _shellProxy.PopModalAsync(animated);
protected override Task<Page> OnPopModal(bool animated) => _shellProxy.PopModalAsync(animated);

protected override Task OnPushModal(Page modal, bool animated) => _shellProxy.PushModalAsync(modal, animated);
}
protected override Task OnPushModal(Page modal, bool animated) => _shellProxy.PushModalAsync(modal, animated);
}
}
}
6 changes: 4 additions & 2 deletions src/Controls/src/Core/Shell/ShellSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ Page IShellSectionController.PresentedPage
{
if (Navigation.ModalStack.Count > 0)
{
if (Navigation.ModalStack[Navigation.ModalStack.Count - 1] is NavigationPage np)
var currentModalPage = Navigation.ModalStack[Navigation.ModalStack.Count - 1];

if (currentModalPage is NavigationPage np)
return np.Navigation.NavigationStack[np.Navigation.NavigationStack.Count - 1];

return Navigation.ModalStack[0];
return currentModalPage;
}

if (_navStack.Count > 1)
Expand Down
49 changes: 49 additions & 0 deletions src/Controls/tests/Core.UnitTests/ShellModalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,55 @@ namespace Microsoft.Maui.Controls.Core.UnitTests

public class ShellModalTests : ShellTestBase
{
[Fact]
public async Task AppearingAndDisappearingFireWhenShellCreatedBeforeWindow()
{
var windowPage = new ContentPage();
var modalPage1 = new ContentPage();
var modalPage2 = new ContentPage();

int modal1Appearing = 0;
int modal1Disappearing = 0;
int modal2Appearing = 0;
int modal2Disappearing = 0;
int windowAppearing = 0;
int windowDisappearing = 0;
int shellAppearing = 0;

modalPage1.Appearing += (_, _) => modal1Appearing++;
modalPage1.Disappearing += (_, _) => modal1Disappearing++;

modalPage2.Appearing += (_, _) => modal2Appearing++;
modalPage2.Disappearing += (_, _) => modal2Disappearing++;

windowPage.Appearing += (_, _) => windowAppearing++;
windowPage.Disappearing += (_, _) => windowDisappearing++;

var shell = new Shell();
shell.Appearing += (_, _) => shellAppearing++;

shell.Items.Add(new ShellContent { Content = windowPage });

await windowPage.Navigation.PushModalAsync(modalPage1);
await windowPage.Navigation.PushModalAsync(modalPage2);

var window = new TestWindow(shell);

await windowPage.Navigation.PopModalAsync();
await windowPage.Navigation.PopModalAsync();

Assert.Equal(1, shellAppearing);

Assert.Equal(1, modal1Appearing);
Assert.Equal(1, modal1Disappearing);

Assert.Equal(1, modal2Appearing);
Assert.Equal(1, modal2Disappearing);

Assert.Equal(1, windowAppearing);
Assert.Equal(0, windowDisappearing);
}

[Fact]
public async Task AppearingAndDisappearingFireOnMultipleModals()
{
Expand Down
28 changes: 28 additions & 0 deletions src/Controls/tests/Core.UnitTests/ShellTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,34 @@ public void GetCurrentPageOnInit()
Assert.NotNull(page);
}

[Fact]
public async Task CurrentPageReturnsCorrectModalPage()
{
var windowPage = new ContentPage();
var modalPage1 = new ContentPage();
var modalPage2 = new ContentPage();

var shell = new TestShell(new ShellContent { Content = windowPage });

await windowPage.Navigation.PushModalAsync(modalPage1);
await windowPage.Navigation.PushModalAsync(modalPage2);

Assert.Equal(shell.Window.Navigation.ModalStack[0], modalPage1);
Assert.Equal(shell.Window.Navigation.ModalStack[1], modalPage2);
Assert.Equal(2, shell.Window.Navigation.ModalStack.Count);
Assert.Equal(modalPage2, shell.CurrentPage);

await windowPage.Navigation.PopModalAsync();

Assert.Equal(shell.Window.Navigation.ModalStack[0], modalPage1);
Assert.Equal(1, shell.Window.Navigation.ModalStack.Count);
Assert.Equal(modalPage1, shell.CurrentPage);

await windowPage.Navigation.PopModalAsync();

Assert.Equal(windowPage, shell.CurrentPage);
}

[Fact]
public async Task HotReloadStaysOnActiveItem()
{
Expand Down
64 changes: 53 additions & 11 deletions src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,70 @@ void SetupBuilder()
{
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(Toolbar), typeof(ToolbarHandler));
handlers.AddHandler(typeof(NavigationPage), typeof(NavigationViewHandler));
handlers.AddHandler(typeof(FlyoutPage), typeof(FlyoutViewHandler));
handlers.AddHandler(typeof(TabbedPage), typeof(TabbedViewHandler));
handlers.AddHandler<Page, PageHandler>();
handlers.AddHandler<Window, WindowHandlerStub>();

SetupShellHandlers(handlers);
handlers.AddHandler(typeof(Controls.Shell), typeof(ShellHandler));
handlers.AddHandler<Layout, LayoutHandler>();
handlers.AddHandler<Entry, EntryHandler>();
handlers.AddHandler<Image, ImageHandler>();
handlers.AddHandler<Label, LabelHandler>();
handlers.AddHandler<Toolbar, ToolbarHandler>();
#if WINDOWS
handlers.AddHandler<ShellItem, ShellItemHandler>();
handlers.AddHandler<ShellSection, ShellSectionHandler>();
handlers.AddHandler<ShellContent, ShellContentHandler>();
#endif
});
});
}

[Fact]
public async Task AppearingAndDisappearingFireWhenShellCreatedBeforeWindow()
{
SetupBuilder();
var windowPage = new ContentPage();
var modalPage1 = new ContentPage();
var modalPage2 = new ContentPage();
var shell = new Shell();

int modal1Appearing = 0;
int modal1Disappearing = 0;
int modal2Appearing = 0;
int modal2Disappearing = 0;
int windowAppearing = 0;
int windowDisappearing = 0;
int shellAppearing = 0;

shell.Appearing += (_, _) => shellAppearing++;
modalPage1.Appearing += (_, _) => modal1Appearing++;
modalPage1.Disappearing += (_, _) => modal1Disappearing++;

modalPage2.Appearing += (_, _) => modal2Appearing++;
modalPage2.Disappearing += (_, _) => modal2Disappearing++;

windowPage.Appearing += (_, _) => windowAppearing++;
windowPage.Disappearing += (_, _) => windowDisappearing++;

shell.Items.Add(new ShellContent { Content = windowPage });

await windowPage.Navigation.PushModalAsync(modalPage1);
await windowPage.Navigation.PushModalAsync(modalPage2);

await CreateHandlerAndAddToWindow<IWindowHandler>(new Window(shell),
async (_) =>
{
await windowPage.Navigation.PopModalAsync();
await windowPage.Navigation.PopModalAsync();
});

// These values should match up with AppearingAndDisappearingFireWhenShellCreatedBeforeWindow
// in the ShellModalTests on the unit tests
Assert.Equal(1, modal1Appearing);
Assert.Equal(1, modal1Disappearing);

Assert.Equal(1, modal2Appearing);
Assert.Equal(1, modal2Disappearing);

Assert.Equal(1, windowAppearing);
Assert.Equal(0, windowDisappearing);
}


[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down