Code/Router.razor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using BetterUI.Extensions;
using Sandbox.Diagnostics;
namespace BetterUI;
/// <summary>
/// A panel that manages child RouterPages. It will only show one page at a time,
/// and automatically adds the page to the DOM.
/// </summary>
public sealed partial class Router : Panel
{
private RouterPage? _currentPage;
private readonly List<RouterPage> _instances = new();
/// <summary>
/// Gets or sets the name of the router. This is used to identify the router.
/// </summary>
public new string Id { get; set; } = null!;
/// <summary>
/// Gets or sets the default page of the router. This is the page that will be shown
/// if no page is specified in the navigation.
/// </summary>
public Type? DefaultPage { get; set; }
protected override void OnAfterTreeRender( bool firstTime )
{
Assert.NotNull( Id, $"{nameof(Router)} must have an {nameof(Id)}" );
foreach ( var page in GetDescendants( this ) )
{
if ( _instances.Exists( x => x.GetType().Name == page.GetType().Name ) )
continue;
page.Style.Position = PositionMode.Absolute;
page.Style.Top = 0;
page.Style.Left = 0;
page.Hide();
_instances.Add( page );
}
if ( !firstTime ) return;
if ( DefaultPage is null ) return;
_currentPage = GetPage( DefaultPage );
_currentPage?.Show();
Scene.RunSceneEvent<IRouterEvent>( x => x.OnRouterReady() );
}
/// <summary>
/// Navigates to a new page.
/// </summary>
/// <param name="type">The type of page to navigate to.</param>
/// <param name="args">Arguments to pass to the page.</param>
/// <returns>The page that was navigated to.</returns>
private RouterPage? Navigate( Type type, params object[] args )
{
var page = GetPage( type );
if ( page is null ) return null;
foreach ( var p in _instances )
p.Style.ZIndex = 0;
if ( _currentPage is not null )
{
Scene.RunSceneEvent<IRouterEvent>( x => x.OnRouterPageClose( _currentPage ) );
_currentPage.Style.ZIndex = 0;
_currentPage.Hide();
}
_currentPage = page;
_currentPage.Show();
_currentPage.Style.ZIndex = 10;
Scene.RunSceneEvent<IRouterEvent>( x => x.OnRouterPageOpen( _currentPage, args ) );
return _currentPage;
}
/// <summary>
/// Navigates to a new page.
/// </summary>
/// <param name="args">Arguments to pass to the page.</param>
/// <returns>The page that was navigated to.</returns>
public T Navigate<T>( params object[] args ) where T : RouterPage, new() => (T)Navigate( typeof(T), args )!;
/// <summary>
/// Checks if the current page is of the specified type.
/// </summary>
/// <returns>True if the current page is of the specified type, false otherwise.</returns>
public bool IsOpen<T>() where T : RouterPage, new() => _currentPage is T;
/// <summary>
/// Retrieves a page of the specified type from the existing instances.
/// </summary>
/// <param name="type">The type of the page to retrieve.</param>
/// <returns>The page of the specified type, or null if not found.</returns>
private RouterPage? GetPage( Type type ) => _instances.FirstOrDefault( x => x.GetType() == type );
/// <summary>
/// Gets a list of all RouterPage descendants within a panel.
/// </summary>
/// <param name="panel">The panel to search within.</param>
/// <returns>A list of RouterPage descendants.</returns>
private static List<RouterPage> GetDescendants( Panel panel ) =>
panel.Descendants.OfType<RouterPage>().ToList();
protected override int BuildHash() => HashCode.Combine( _instances.Count, _currentPage );
}