Skip to content
10 changes: 9 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@ jobs:
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Pack
run: dotnet pack --configuration Release --no-restore
- name: Save Artifact
uses: actions/upload-artifact@v3
with:
name: nupkg
path: ./Float.Core/obj/Release/
retention-days: 5
- name: Test
run: dotnet test --configuration Release --no-build --verbosity normal --logger:"trx;"
run: dotnet test --configuration MAUI --no-restore -f net7.0 --no-build --verbosity normal --logger:"trx;"
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
Expand Down
2 changes: 1 addition & 1 deletion Float.Core.Tests/Float.Core.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netcoreapp3.1;net7.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>9.0</LangVersion>
<UseMaui>true</UseMaui>
Expand Down
25 changes: 20 additions & 5 deletions Float.Core.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,42 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Float.Core", "Float.Core\Float.Core.csproj", "{0AF193E3-D9AF-4551-B853-177F15FDC639}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Float.Core.Tests", "Float.Core.Tests\Float.Core.Tests.csproj", "{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}"
Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "Float.Core.Tests", "Float.Core.Tests\Float.Core.Tests.csproj", "{0302630F-9229-48C3-BF66-0A8E7384A3DF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
MAUI|Any CPU = MAUI|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.Release|Any CPU.Build.0 = Release|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD4538B9-EBD5-4340-B44A-890DDCEFC31E}.Release|Any CPU.Build.0 = Release|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.MAUI|Any CPU.ActiveCfg = MAUI|Any CPU
{0AF193E3-D9AF-4551-B853-177F15FDC639}.MAUI|Any CPU.Build.0 = MAUI|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.Release|Any CPU.Build.0 = Release|Any CPU
{0302630F-9229-48C3-BF66-0A8E7384A3DF}.MAUI|Any CPU.ActiveCfg = MAUI|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.StandardHeader = $1
$1.IncludeInNewFiles = False
$0.VersionControlPolicy = $2
$0.DotNetNamingPolicy = $3
$3.DirectoryNamespaceAssociation = PrefixedHierarchical
$0.TextStylePolicy = $4
$4.inheritsSet = null
$4.scope = text/x-csharp
$0.CSharpFormattingPolicy = $5
$5.scope = text/x-csharp
$0.TextStylePolicy = $6
$6.FileWidth = 80
$6.TabsToSpaces = True
$6.scope = text/plain
EndGlobalSection
EndGlobal
20 changes: 19 additions & 1 deletion Float.Core/Float.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2;net7.0</TargetFrameworks>
<AssemblyName>Float.Core</AssemblyName>
<AssemblyAuthor>Float</AssemblyAuthor>
<AssemblyDescription>Common utility code used by Float projects.</AssemblyDescription>
Expand All @@ -14,6 +14,7 @@
<ReleaseVersion>1.0.0</ReleaseVersion>
<Version></Version>
<UseMaui>true</UseMaui>
<Configurations>Release;MAUI;Debug</Configurations>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="$(SolutionDir)\stylecop.json" />
Expand All @@ -31,6 +32,23 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>readme.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>TRACE;RELEASE;</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'MAUI|AnyCPU' ">
<IntermediateOutputPath>obj\MAUI</IntermediateOutputPath>
<DebugSymbols>true</DebugSymbols>
<Optimize>false</Optimize>
<OutputPath>bin\MAUI</OutputPath>
<DefineConstants>TRACE;DEBUG;MAUI;</DefineConstants>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\MAUI\Float.Core.xml</DocumentationFile>
<NoWarn></NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>TRACE;DEBUG;</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Float.TinCan" Version="1.0.3.30" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
Expand Down
56 changes: 53 additions & 3 deletions Float.Core/UX/Coordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ public abstract class Coordinator : ICoordinator
/// <value><c>true</c> if is finished; otherwise, <c>false</c>.</value>
protected bool IsFinished => isFinished;

/// <summary>
/// Gets or sets the event args that are pending a finish.
/// </summary>
/// <value>
/// The event args that are pending a finish.
/// </value>
protected EventArgs WaitingToFinishEventArgs { get; set; }

/// <inheritdoc />
public virtual void Start(INavigationContext context)
{
Expand Down Expand Up @@ -98,6 +106,36 @@ public virtual void Start()
isStarted = true;
}

/// <inheritdoc/>
public ICoordinator.CoordinatorRequestFinishStatus RequestFinish(EventArgs args)
{
return HandleFinishRequested(this, args);
}

/// <summary>
/// Handles when a finish is requested.
/// </summary>
/// <param name="coordinator">The coordinator that has requested this coordinator to finish.</param>
/// <param name="eventArgs">The event args.</param>
/// <returns>A value indicating whether this finished.</returns>
protected virtual ICoordinator.CoordinatorRequestFinishStatus HandleFinishRequested(ICoordinator coordinator, EventArgs eventArgs)
{
if (this == coordinator)
{
if (managedPage == null)
{
Finish(eventArgs);
return ICoordinator.CoordinatorRequestFinishStatus.FinishedImmediately;
}

WaitingToFinishEventArgs = eventArgs;
NavigationContext.Reset(false);
return ICoordinator.CoordinatorRequestFinishStatus.PendingFinish;
}

return ICoordinator.CoordinatorRequestFinishStatus.WillNotFinish;
}

/// <summary>
/// Returning a page here will allow the coordinator to automatically
/// manage itself based on the state of the UI.
Expand Down Expand Up @@ -131,6 +169,7 @@ protected virtual void Finish(EventArgs args)
}

managedPage = null;
WaitingToFinishEventArgs = null;

if (NavigationContext != null)
{
Expand Down Expand Up @@ -210,15 +249,26 @@ void HandleNavigation(object sender, NavigationEventArgs args)
case NavigationEventArgs.NavigationType.Popped:
if (args.Page == managedPage && !IsFinished)
{
Finish(EventArgs.Empty);
if (WaitingToFinishEventArgs == null)
{
Finish(EventArgs.Empty);
return;
}

Finish(WaitingToFinishEventArgs);
}

break;

case NavigationEventArgs.NavigationType.Reset:
if (args.Page != managedPage && !IsFinished)
{
Finish(EventArgs.Empty);
if (WaitingToFinishEventArgs == null)
{
Finish(EventArgs.Empty);
return;
}

Finish(WaitingToFinishEventArgs);
}

break;
Expand Down
22 changes: 22 additions & 0 deletions Float.Core/UX/CoordinatorParent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,22 @@ public override string ToString()
return $"[{GetType()}, Children: {string.Join(",", childCoordinators)}]";
}

/// <inheritdoc />
protected override ICoordinator.CoordinatorRequestFinishStatus HandleFinishRequested(ICoordinator coordinator, EventArgs eventArgs)
{
WaitingToFinishEventArgs = eventArgs;

foreach (var eachChild in ChildCoordinators)
{
if (eachChild is Coordinator childCoordinator)
{
childCoordinator.RequestFinish(eventArgs);
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EBusch We're probably missing a call to the base implementation here. When you add that, you may have an issue with Finish getting called twice:

Coordinator.HandleNavigation may call it and so will CoordinatorParent.HandleChildFinish -- it is not currently clear to me the best way to address that.

return base.HandleFinishRequested(coordinator, eventArgs);
}

/// <inheritdoc />
protected override void Finish(EventArgs args)
{
Expand Down Expand Up @@ -149,6 +165,12 @@ protected virtual void HandleChildFinish(object sender, EventArgs args)
{
RemoveChild(child);
}

if (!HasChildren && WaitingToFinishEventArgs != null)
{
Finish(WaitingToFinishEventArgs);
WaitingToFinishEventArgs = null;
}
}
}
}
33 changes: 33 additions & 0 deletions Float.Core/UX/ICoordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,43 @@ public interface ICoordinator
/// </summary>
event EventHandler<EventArgs> Finished;

/// <summary>
/// An enum for the possible results of a coordinator's RequestFinish.
/// </summary>
public enum CoordinatorRequestFinishStatus
{
/// <summary>
/// The coordinator RequestFinish finished immediately.
/// </summary>
FinishedImmediately,

/// <summary>
/// The coordinator RequestFinish is pending a finish.
/// </summary>
PendingFinish,

/// <summary>
/// The coordinator RequestFinish will not complete.
/// </summary>
WillNotFinish,

/// <summary>
/// The coordinator RequestFinish's status is unknown.
/// </summary>
Unknown,
}

/// <summary>
/// Implementing classes should use this to start this coordinator.
/// </summary>
/// <param name="navigationContext">The navigation context for this coordinator.</param>
void Start(INavigationContext navigationContext);

/// <summary>
/// Requests that a coordinator will finish.
/// </summary>
/// <param name="eventArgs">The event args.</param>
/// <returns>A task, with a boolean indicating whether this did finish.</returns>
CoordinatorRequestFinishStatus RequestFinish(EventArgs eventArgs);
}
}