diff --git a/Fabulous.MauiControls.sln b/Fabulous.MauiControls.sln
index c209e67..f747b8c 100644
--- a/Fabulous.MauiControls.sln
+++ b/Fabulous.MauiControls.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
@@ -47,6 +47,12 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TicTacComponent", "samples\
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MultipleMvus", "samples\Components\MultipleMvus\MultipleMvus.fsproj", "{C3B4F669-AD4D-4C2F-B053-F5E34E0D9342}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Navigation", "Navigation", "{3A3581BD-4228-49B0-84D5-AF39D620BA34}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "BasicNavigation", "samples\Navigation\BasicNavigation\BasicNavigation.fsproj", "{CE61493B-86CC-49CE-9443-F25F1ECB15C9}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "NavigationPath", "samples\Navigation\NavigationPath\NavigationPath.fsproj", "{5B3F6C4E-82CF-442F-BFB4-216C1CD85700}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -114,6 +120,14 @@ Global
{C3B4F669-AD4D-4C2F-B053-F5E34E0D9342}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3B4F669-AD4D-4C2F-B053-F5E34E0D9342}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3B4F669-AD4D-4C2F-B053-F5E34E0D9342}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CE61493B-86CC-49CE-9443-F25F1ECB15C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE61493B-86CC-49CE-9443-F25F1ECB15C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE61493B-86CC-49CE-9443-F25F1ECB15C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CE61493B-86CC-49CE-9443-F25F1ECB15C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5B3F6C4E-82CF-442F-BFB4-216C1CD85700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5B3F6C4E-82CF-442F-BFB4-216C1CD85700}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5B3F6C4E-82CF-442F-BFB4-216C1CD85700}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5B3F6C4E-82CF-442F-BFB4-216C1CD85700}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{67FB01A1-1A3E-4A3B-83DC-7D63B56FB1A1} = {35A6823C-8312-4F92-818A-5117BB31A569}
@@ -129,5 +143,8 @@ Global
{2BA5E664-1516-4B35-B5BD-1257A1B6C5A1} = {D3D3AA5A-1A1B-4E2F-92D7-491706251302}
{DF4CDFC2-1BC7-43FE-BE2B-B48AA3202FC6} = {D3D3AA5A-1A1B-4E2F-92D7-491706251302}
{C3B4F669-AD4D-4C2F-B053-F5E34E0D9342} = {D3D3AA5A-1A1B-4E2F-92D7-491706251302}
+ {3A3581BD-4228-49B0-84D5-AF39D620BA34} = {87C8E9E8-497E-46DB-90FE-4402E0CB230A}
+ {CE61493B-86CC-49CE-9443-F25F1ECB15C9} = {3A3581BD-4228-49B0-84D5-AF39D620BA34}
+ {5B3F6C4E-82CF-442F-BFB4-216C1CD85700} = {3A3581BD-4228-49B0-84D5-AF39D620BA34}
EndGlobalSection
EndGlobal
diff --git a/samples/Components/HelloComponent/App.fs b/samples/Components/HelloComponent/App.fs
index d8d1be2..ecb9da9 100644
--- a/samples/Components/HelloComponent/App.fs
+++ b/samples/Components/HelloComponent/App.fs
@@ -7,7 +7,7 @@ open type Fabulous.Maui.View
module App =
let view () =
- Component() { Application() { ContentPage() { Label("Hello Component").center() } } }
+ Component() { Application(ContentPage() { Label("Hello Component").center() }) }
let createMauiApp () =
MauiApp.CreateBuilder().UseFabulousApp(view).Build()
diff --git a/samples/Components/MultipleMvus/App.fs b/samples/Components/MultipleMvus/App.fs
index 36a52a2..d09d3af 100644
--- a/samples/Components/MultipleMvus/App.fs
+++ b/samples/Components/MultipleMvus/App.fs
@@ -39,7 +39,7 @@ module Form =
module App =
let view () =
- Application() {
+ Application(
ContentPage() {
(VStack(spacing = 25.) {
Label("App")
@@ -67,4 +67,4 @@ module App =
.width(250.)
.center()
}
- }
+ )
diff --git a/samples/Components/MvuCounter/App.fs b/samples/Components/MvuCounter/App.fs
index ef77057..880bb86 100644
--- a/samples/Components/MvuCounter/App.fs
+++ b/samples/Components/MvuCounter/App.fs
@@ -55,7 +55,7 @@ module App =
Component(program) {
let! model = Mvu.State
- Application() {
+ Application(
ContentPage() {
(VStack() {
Label($"%d{model.Count}").centerTextHorizontal()
@@ -80,5 +80,5 @@ module App =
})
.center()
}
- }
+ )
}
diff --git a/samples/Navigation/BasicNavigation/BasicNavigation.fsproj b/samples/Navigation/BasicNavigation/BasicNavigation.fsproj
new file mode 100644
index 0000000..3fd59ae
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/BasicNavigation.fsproj
@@ -0,0 +1,105 @@
+
+
+
+ net8.0-android;net8.0-ios;net8.0-maccatalyst
+ $(TargetFrameworks);net8.0-windows10.0.19041.0
+
+
+ Exe
+ NavigationSample
+ true
+ true
+ false
+
+
+ NavigationSample
+
+
+ org.fabulous.maui.basicnavigation
+ c6c98ddf-a204-4a7e-be03-2829f110f396
+
+
+ 1.0
+ 1
+
+ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))
+
+ 14.2
+ 14.0
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Navigation/BasicNavigation/MauiProgram.fs b/samples/Navigation/BasicNavigation/MauiProgram.fs
new file mode 100644
index 0000000..9365d83
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/MauiProgram.fs
@@ -0,0 +1,16 @@
+namespace NavigationSample
+
+open Microsoft.Maui.Hosting
+open Fabulous.Maui
+
+type MauiProgram =
+ static member CreateMauiApp() =
+ MauiApp
+ .CreateBuilder()
+ .UseFabulousApp(Sample.program)
+ .ConfigureFonts(fun fonts ->
+ fonts
+ .AddFont("OpenSans-Regular.ttf", "OpenSansRegular")
+ .AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold")
+ |> ignore)
+ .Build()
diff --git a/samples/Navigation/BasicNavigation/PageA.fs b/samples/Navigation/BasicNavigation/PageA.fs
new file mode 100644
index 0000000..05b2842
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/PageA.fs
@@ -0,0 +1,29 @@
+namespace NavigationSample
+
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+module PageA =
+ type Model = { Count: int }
+
+ type Msg =
+ | Increment
+ | Decrement
+
+ let init () = { Count = 0 }
+
+ let update msg model =
+ match msg with
+ | Increment -> { Count = model.Count + 1 }
+ | Decrement -> { Count = model.Count - 1 }
+
+ let view model =
+ VStack() {
+ Label("Page A").font(32.).centerTextHorizontal().margin(0., 0., 0., 30.)
+
+ Label($"Count: {model.Count}").centerTextHorizontal()
+
+ Button("Increment", Increment)
+ Button("Decrement", Decrement)
+ }
diff --git a/samples/Navigation/BasicNavigation/PageB.fs b/samples/Navigation/BasicNavigation/PageB.fs
new file mode 100644
index 0000000..61a8f4f
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/PageB.fs
@@ -0,0 +1,29 @@
+namespace NavigationSample
+
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+module PageB =
+ type Model = { Count: int }
+
+ type Msg =
+ | Increment
+ | Decrement
+
+ let init () = { Count = 0 }
+
+ let update msg model =
+ match msg with
+ | Increment -> { Count = model.Count + 1 }
+ | Decrement -> { Count = model.Count - 1 }
+
+ let view model =
+ VStack() {
+ Label("Page B").font(32.).centerTextHorizontal().margin(0., 0., 0., 30.)
+
+ Label($"Count: {model.Count}").centerTextHorizontal()
+
+ Button("Increment", Increment)
+ Button("Decrement", Decrement)
+ }
diff --git a/samples/Navigation/BasicNavigation/PageC.fs b/samples/Navigation/BasicNavigation/PageC.fs
new file mode 100644
index 0000000..0503667
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/PageC.fs
@@ -0,0 +1,29 @@
+namespace NavigationSample
+
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+module PageC =
+ type Model = { Count: int }
+
+ type Msg =
+ | Increment
+ | Decrement
+
+ let init () = { Count = 0 }
+
+ let update msg model =
+ match msg with
+ | Increment -> { Count = model.Count + 1 }
+ | Decrement -> { Count = model.Count - 1 }
+
+ let view model =
+ VStack() {
+ Label("Page C").font(32.).centerTextHorizontal().margin(0., 0., 0., 30.)
+
+ Label($"Count: {model.Count}").centerTextHorizontal()
+
+ Button("Increment", Increment)
+ Button("Decrement", Decrement)
+ }
diff --git a/samples/Navigation/BasicNavigation/Platforms/Android/AndroidManifest.xml b/samples/Navigation/BasicNavigation/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..bdec9b5
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/BasicNavigation/Platforms/Android/MainActivity.fs b/samples/Navigation/BasicNavigation/Platforms/Android/MainActivity.fs
new file mode 100644
index 0000000..85f15ab
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Android/MainActivity.fs
@@ -0,0 +1,17 @@
+namespace NavigationSample
+
+open Android.App
+open Android.Content.PM
+open Microsoft.Maui
+
+[]
+type MainActivity() =
+ inherit MauiAppCompatActivity()
diff --git a/samples/Navigation/BasicNavigation/Platforms/Android/MainApplication.fs b/samples/Navigation/BasicNavigation/Platforms/Android/MainApplication.fs
new file mode 100644
index 0000000..7910a54
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Android/MainApplication.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample
+
+open Android.App
+open Microsoft.Maui
+
+[]
+type MainApplication(handle, ownership) =
+ inherit MauiApplication(handle, ownership)
+
+ override _.CreateMauiApp() = MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/BasicNavigation/Platforms/Android/Resources/values/colors.xml b/samples/Navigation/BasicNavigation/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..5cd1604
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/AppDelegate.fs b/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/AppDelegate.fs
new file mode 100644
index 0000000..90458e5
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/AppDelegate.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample
+
+open Foundation
+open Microsoft.Maui
+
+[]
+type AppDelegate() =
+ inherit MauiUIApplicationDelegate()
+
+ override this.CreateMauiApp() = MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/Info.plist b/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000..0690e47
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/Program.fs b/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/Program.fs
new file mode 100644
index 0000000..26c87e7
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/MacCatalyst/Program.fs
@@ -0,0 +1,9 @@
+namespace NavigationSample
+
+open UIKit
+
+module Program =
+ []
+ let main args =
+ UIApplication.Main(args, null, typeof)
+ 0
diff --git a/samples/Navigation/BasicNavigation/Platforms/Tizen/Main.fs b/samples/Navigation/BasicNavigation/Platforms/Tizen/Main.fs
new file mode 100644
index 0000000..3d59efd
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Tizen/Main.fs
@@ -0,0 +1,16 @@
+namespace NavigationSample
+
+open System
+open Microsoft.Maui
+open Microsoft.Maui.Hosting
+
+type Program() =
+ inherit MauiApplication()
+
+ override this.CreateMauiApp() = MauiProgram.CreateMauiApp()
+
+module Program =
+ []
+ let main args =
+ let app = Program()
+ app.Run(args)
diff --git a/samples/Navigation/BasicNavigation/Platforms/Tizen/tizen-manifest.xml b/samples/Navigation/BasicNavigation/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 0000000..83e2649
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ appicon.xhigh.png
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/BasicNavigation/Platforms/Windows/App.fs b/samples/Navigation/BasicNavigation/Platforms/Windows/App.fs
new file mode 100644
index 0000000..ebceb21
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Windows/App.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample.WinUI
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+type App() =
+ inherit FSharp.Maui.WinUICompat.App()
+
+ override this.CreateMauiApp() =
+ NavigationSample.MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/BasicNavigation/Platforms/Windows/Main.fs b/samples/Navigation/BasicNavigation/Platforms/Windows/Main.fs
new file mode 100644
index 0000000..501c15d
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Windows/Main.fs
@@ -0,0 +1,9 @@
+namespace NavigationSample.WinUI
+
+open System
+
+module Program =
+ []
+ let main args =
+ do FSharp.Maui.WinUICompat.Program.Main(args, typeof)
+ 0
diff --git a/samples/Navigation/BasicNavigation/Platforms/Windows/Package.appxmanifest b/samples/Navigation/BasicNavigation/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..e98c6ed
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Navigation/BasicNavigation/Platforms/Windows/app.manifest b/samples/Navigation/BasicNavigation/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..c8a173c
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/samples/Navigation/BasicNavigation/Platforms/iOS/AppDelegate.fs b/samples/Navigation/BasicNavigation/Platforms/iOS/AppDelegate.fs
new file mode 100644
index 0000000..7418834
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/iOS/AppDelegate.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample
+
+open Foundation
+open Microsoft.Maui
+
+[]
+type AppDelegate() =
+ inherit MauiUIApplicationDelegate()
+
+ override _.CreateMauiApp() = MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/BasicNavigation/Platforms/iOS/Info.plist b/samples/Navigation/BasicNavigation/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..358337b
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/samples/Navigation/BasicNavigation/Platforms/iOS/Program.fs b/samples/Navigation/BasicNavigation/Platforms/iOS/Program.fs
new file mode 100644
index 0000000..26c87e7
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Platforms/iOS/Program.fs
@@ -0,0 +1,9 @@
+namespace NavigationSample
+
+open UIKit
+
+module Program =
+ []
+ let main args =
+ UIApplication.Main(args, null, typeof)
+ 0
diff --git a/samples/Navigation/BasicNavigation/Properties/launchSettings.json b/samples/Navigation/BasicNavigation/Properties/launchSettings.json
new file mode 100644
index 0000000..c16206a
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/Navigation/BasicNavigation/Resources/AppIcon/appicon.svg b/samples/Navigation/BasicNavigation/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..5f04fcf
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/samples/Navigation/BasicNavigation/Resources/AppIcon/appiconfg.svg b/samples/Navigation/BasicNavigation/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/BasicNavigation/Resources/Fonts/OpenSans-Regular.ttf b/samples/Navigation/BasicNavigation/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..534d009
Binary files /dev/null and b/samples/Navigation/BasicNavigation/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/samples/Navigation/BasicNavigation/Resources/Fonts/OpenSans-Semibold.ttf b/samples/Navigation/BasicNavigation/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..f333153
Binary files /dev/null and b/samples/Navigation/BasicNavigation/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/samples/Navigation/BasicNavigation/Resources/Images/dotnet_bot.svg b/samples/Navigation/BasicNavigation/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000..51b1c33
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/samples/Navigation/BasicNavigation/Resources/Raw/AboutAssets.txt b/samples/Navigation/BasicNavigation/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..50b8a7b
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/samples/Navigation/BasicNavigation/Resources/Splash/splash.svg b/samples/Navigation/BasicNavigation/Resources/Splash/splash.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/BasicNavigation/Sample.fs b/samples/Navigation/BasicNavigation/Sample.fs
new file mode 100644
index 0000000..7cb29de
--- /dev/null
+++ b/samples/Navigation/BasicNavigation/Sample.fs
@@ -0,0 +1,80 @@
+namespace NavigationSample
+
+open Fabulous
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+/// The most basic navigation with Fabulous is done by swapping widgets.
+///
+/// In this sample, we have three pages, and we swap between them by changing the "current step" (represented by a discriminated union).
+/// All three pages' models are loaded at the same time, but only the current page is rendered.
+///
+/// NOTE: This approach is not using any official navigation system.
+/// Hence there is no back button, no navigation history, no animations, etc by default.
+/// Fabulous will only give the illusion of navigation by swapping the pages.
+module Sample =
+ []
+ type Step =
+ | PageA
+ | PageB
+ | PageC
+
+ type Model =
+ { CurrentStep: Step
+ PageAModel: PageA.Model
+ PageBModel: PageB.Model
+ PageCModel: PageC.Model }
+
+ type Msg =
+ | PageAMsg of PageA.Msg
+ | PageBMsg of PageB.Msg
+ | PageCMsg of PageC.Msg
+ | GoToPageA
+ | GoToPageB
+ | GoToPageC
+
+ let init () =
+ { CurrentStep = Step.PageA
+ PageAModel = PageA.init()
+ PageBModel = PageB.init()
+ PageCModel = PageC.init() }
+
+ let update msg model =
+ match msg with
+ | PageAMsg msg ->
+ { model with
+ PageAModel = PageA.update msg model.PageAModel }
+ | PageBMsg msg ->
+ { model with
+ PageBModel = PageB.update msg model.PageBModel }
+ | PageCMsg msg ->
+ { model with
+ PageCModel = PageC.update msg model.PageCModel }
+ | GoToPageA -> { model with CurrentStep = Step.PageA }
+ | GoToPageB -> { model with CurrentStep = Step.PageB }
+ | GoToPageC -> { model with CurrentStep = Step.PageC }
+
+ let view model =
+ Application(
+ ContentPage(
+ (Grid(coldefs = [ Star; Star; Star ], rowdefs = [ Auto; Star ]) {
+ Button("Page A", GoToPageA).gridColumn(0)
+
+ Button("Page B", GoToPageB).gridColumn(1)
+
+ Button("Page C", GoToPageC).gridColumn(2)
+
+
+ (match model.CurrentStep with
+ | Step.PageA -> View.map PageAMsg (PageA.view model.PageAModel)
+ | Step.PageB -> View.map PageBMsg (PageB.view model.PageBModel)
+ | Step.PageC -> View.map PageCMsg (PageC.view model.PageCModel))
+ .gridRow(1)
+ .gridColumnSpan(3)
+ })
+ .rowSpacing(30.)
+ )
+ )
+
+ let program = Program.stateful init update |> Program.withView view
diff --git a/samples/Navigation/NavigationPath/MauiProgram.fs b/samples/Navigation/NavigationPath/MauiProgram.fs
new file mode 100644
index 0000000..9365d83
--- /dev/null
+++ b/samples/Navigation/NavigationPath/MauiProgram.fs
@@ -0,0 +1,16 @@
+namespace NavigationSample
+
+open Microsoft.Maui.Hosting
+open Fabulous.Maui
+
+type MauiProgram =
+ static member CreateMauiApp() =
+ MauiApp
+ .CreateBuilder()
+ .UseFabulousApp(Sample.program)
+ .ConfigureFonts(fun fonts ->
+ fonts
+ .AddFont("OpenSans-Regular.ttf", "OpenSansRegular")
+ .AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold")
+ |> ignore)
+ .Build()
diff --git a/samples/Navigation/NavigationPath/NavigationPath.fsproj b/samples/Navigation/NavigationPath/NavigationPath.fsproj
new file mode 100644
index 0000000..603c0a9
--- /dev/null
+++ b/samples/Navigation/NavigationPath/NavigationPath.fsproj
@@ -0,0 +1,107 @@
+
+
+
+ net8.0-android;net8.0-ios;net8.0-maccatalyst
+ $(TargetFrameworks);net8.0-windows10.0.19041.0
+
+
+ Exe
+ NavigationSample
+ true
+ true
+ false
+
+
+ NavigationSample
+
+
+ org.fabulous.maui.basicnavigation
+ 95540b31-0bdf-4c31-a775-610721869c08
+
+
+ 1.0
+ 1
+
+ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))
+
+ 14.2
+ 14.0
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Navigation/NavigationPath/NavigationState.fs b/samples/Navigation/NavigationPath/NavigationState.fs
new file mode 100644
index 0000000..2c237ff
--- /dev/null
+++ b/samples/Navigation/NavigationPath/NavigationState.fs
@@ -0,0 +1,83 @@
+namespace NavigationSample
+
+open Fabulous
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+/// MVU is a very explicit but also very verbose pattern.
+/// Everything needs to be explicitly written out.
+///
+/// This NavigationState file acts as the glue between the different pages.
+/// Relying on the NavigationPath DU, we call the init, update and view functions of the different pages.
+/// Like this, everything is centralized and the root of the app doesn't have to know about the other pages.
+module NavigationState =
+ type Model =
+ | PageAModel of PageA.Model
+ | PageBModel of PageB.Model
+ | PageCModel of PageC.Model
+
+ type Msg =
+ | PageAMsg of PageA.Msg
+ | PageBMsg of PageB.Msg
+ | PageCMsg of PageC.Msg
+
+ let init path =
+ match path with
+ | NavigationPath.PageA -> PageAModel(PageA.init())
+ | NavigationPath.PageB initialCount -> PageBModel(PageB.init initialCount)
+ | NavigationPath.PageC(someArgs, stepCount) -> PageCModel(PageC.init someArgs stepCount)
+
+ let update nav (msg: Msg) (model: Model) =
+ match msg, model with
+ | PageAMsg msg, PageAModel model ->
+ let m, c = PageA.update nav msg model
+ PageAModel m, Cmd.map PageAMsg c
+
+ | PageBMsg msg, PageBModel model ->
+ let m, c = PageB.update nav msg model
+ PageBModel m, Cmd.map PageBMsg c
+
+ | PageCMsg msg, PageCModel model ->
+ let m, c = PageC.update nav msg model
+ PageCModel m, Cmd.map PageCMsg c
+
+ | _ -> model, Cmd.none
+
+ let view model =
+ match model with
+ | PageAModel model -> AnyPage(View.map PageAMsg (PageA.view model))
+ | PageBModel model -> AnyPage(View.map PageBMsg (PageB.view model))
+ | PageCModel model -> AnyPage(View.map PageCMsg (PageC.view model))
+
+ let updateBackButton nav model =
+ match model with
+ | PageAModel model -> update nav (PageAMsg PageA.BackButtonPressed) (PageAModel model)
+ | _ -> model, Cmd.none
+
+/// The NavigationStack represents the history of the navigation.
+/// This is a simple stack of pages that the app will use to remember and display the pages needed.
+type NavigationStack =
+ { BackStack: NavigationState.Model list
+ CurrentPage: NavigationState.Model
+ ForwardStack: NavigationState.Model list }
+
+ static member Init(model: NavigationState.Model) =
+ { BackStack = []
+ CurrentPage = model
+ ForwardStack = [] }
+
+ member this.Push(model: NavigationState.Model) =
+ { BackStack = this.CurrentPage :: this.BackStack
+ CurrentPage = model
+ ForwardStack = [] }
+
+ member this.Pop() =
+ match this.BackStack with
+ | [] -> this
+ | head :: tail ->
+ { BackStack = tail
+ CurrentPage = head
+ ForwardStack = [] }
+
+ member this.UpdateCurrentPage(newPage: NavigationState.Model) = { this with CurrentPage = newPage }
diff --git a/samples/Navigation/NavigationPath/NavigationTypes.fs b/samples/Navigation/NavigationPath/NavigationTypes.fs
new file mode 100644
index 0000000..241d6d8
--- /dev/null
+++ b/samples/Navigation/NavigationPath/NavigationTypes.fs
@@ -0,0 +1,39 @@
+namespace NavigationSample
+
+open Fabulous
+
+/// This is the centerpiece of navigating through paths:
+/// A single enum regrouping all the navigation routes with their arguments
+[]
+type NavigationPath =
+ | PageA
+ | PageB of initialCount: int
+ | PageC of someArgs: string * stepCount: int
+
+/// The NavigationController is used to notify the intention to navigate to a new page (or go back).
+/// We listen to it in a Cmd that will dispatch a message to the root of the application to trigger the actual navigation.
+type NavigationController() =
+ let navigated = Event()
+ let backNavigated = Event()
+
+ member this.Navigated = navigated.Publish
+ member this.BackNavigated = backNavigated.Publish
+
+ member this.NavigateTo(path: NavigationPath) = navigated.Trigger(path)
+
+ member this.NavigateBack() = backNavigated.Trigger()
+
+/// The Navigation module is a set of helper functions that will wrap the call to NavigationController into a Cmd.
+/// We do that because navigation is a side-effect and we want to keep it in a Cmd.
+module Navigation =
+ let private navigateTo (nav: NavigationController) path : Cmd<'msg> = [ fun _ -> nav.NavigateTo(path) ]
+
+ let navigateBack (nav: NavigationController) : Cmd<'msg> = [ fun _ -> nav.NavigateBack() ]
+
+ let navigateToPageA nav = navigateTo nav NavigationPath.PageA
+
+ let navigateToPageB nav initialCount =
+ navigateTo nav (NavigationPath.PageB initialCount)
+
+ let navigateToPageC nav someArgs stepCount =
+ navigateTo nav (NavigationPath.PageC(someArgs, stepCount))
diff --git a/samples/Navigation/NavigationPath/PageA.fs b/samples/Navigation/NavigationPath/PageA.fs
new file mode 100644
index 0000000..89bd3a5
--- /dev/null
+++ b/samples/Navigation/NavigationPath/PageA.fs
@@ -0,0 +1,51 @@
+namespace NavigationSample
+
+open Fabulous
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+/// Each pages are "isolated". They have their own MVU loop and own types.
+/// The only dependency they receive from outside is the NavigationController, which is passed to the update function.
+module PageA =
+ type Model = { Count: int }
+
+ type Msg =
+ | Increment
+ | Decrement
+ | GoBack
+ | GoToPageB
+ | GoToPageC
+ | BackButtonPressed
+
+ /// Since the NavigationPath.PageA doesn't take arguments, the init function excepts a unit parameter.
+ let init () = { Count = 0 }
+
+ let update (nav: NavigationController) msg model =
+ match msg with
+ | Increment -> { Count = model.Count + 1 }, Cmd.none
+ | Decrement -> { Count = model.Count - 1 }, Cmd.none
+ | GoBack -> model, Navigation.navigateBack nav
+ | GoToPageB -> model, Navigation.navigateToPageB nav model.Count
+ | GoToPageC -> model, Navigation.navigateToPageC nav "Hello from Page A!" model.Count
+ | BackButtonPressed -> { Count = model.Count - 1 }, Cmd.none
+
+ let view model =
+ ContentPage(
+ Grid(coldefs = [ Star ], rowdefs = [ Star; Auto ]) {
+ VStack() {
+ Label($"Count: {model.Count}").centerTextHorizontal()
+
+ Button("Increment", Increment)
+ Button("Decrement", Decrement)
+ }
+
+ (VStack() {
+ Button("Go back", GoBack)
+ Button("Go to Page B", GoToPageB)
+ Button("Go to Page C", GoToPageC)
+ })
+ .gridRow(1)
+ }
+ )
+ .title("Page A")
diff --git a/samples/Navigation/NavigationPath/PageB.fs b/samples/Navigation/NavigationPath/PageB.fs
new file mode 100644
index 0000000..6ca5275
--- /dev/null
+++ b/samples/Navigation/NavigationPath/PageB.fs
@@ -0,0 +1,51 @@
+namespace NavigationSample
+
+open Fabulous
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+module PageB =
+ type Model = { InitialCount: int; Count: int }
+
+ type Msg =
+ | Increment
+ | Decrement
+ | GoBack
+ | GoToPageA
+ | GoToPageC
+
+ /// Contrary to PageA, NavigationPath.PageB has a initialCount argument so the init function will receive it.
+ let init initialCount =
+ { InitialCount = initialCount
+ Count = initialCount }
+
+ let update (nav: NavigationController) msg model =
+ match msg with
+ | Increment -> { model with Count = model.Count + 1 }, Cmd.none
+ | Decrement -> { model with Count = model.Count - 1 }, Cmd.none
+ | GoBack -> model, Navigation.navigateBack nav
+ | GoToPageA -> model, Navigation.navigateToPageA nav
+ | GoToPageC -> model, Navigation.navigateToPageC nav "Hello from Page A!" model.Count
+
+ let view model =
+ ContentPage(
+ Grid(coldefs = [ Star ], rowdefs = [ Star; Auto ]) {
+ VStack() {
+ Label($"Initial count: {model.InitialCount}")
+
+ Label($"Count: {model.Count}").centerTextHorizontal()
+
+ Button("Increment", Increment)
+ Button("Decrement", Decrement)
+ }
+
+ (VStack() {
+ Button("Go back", GoBack)
+ Button("Go to Page A", GoToPageA)
+ Button("Go to Page C", GoToPageC)
+ })
+ .gridRow(1)
+ }
+ )
+ .title("Page B")
diff --git a/samples/Navigation/NavigationPath/PageC.fs b/samples/Navigation/NavigationPath/PageC.fs
new file mode 100644
index 0000000..2004ac0
--- /dev/null
+++ b/samples/Navigation/NavigationPath/PageC.fs
@@ -0,0 +1,61 @@
+namespace NavigationSample
+
+open Fabulous
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+module PageC =
+ type Model =
+ { Args: string
+ StepCount: int
+ Count: int }
+
+ type Msg =
+ | Increment
+ | Decrement
+ | GoBack
+ | GoToPageA
+ | GoToPageB
+
+ let init args stepCount =
+ { Args = args
+ StepCount = stepCount
+ Count = 0 }
+
+ let update (nav: NavigationController) msg model =
+ match msg with
+ | Increment ->
+ { model with
+ Count = model.Count + model.StepCount },
+ Cmd.none
+ | Decrement ->
+ { model with
+ Count = model.Count - model.StepCount },
+ Cmd.none
+ | GoBack -> model, Navigation.navigateBack nav
+ | GoToPageA -> model, Navigation.navigateToPageA nav
+ | GoToPageB -> model, Navigation.navigateToPageB nav model.Count
+
+ let view model =
+ ContentPage(
+ Grid(coldefs = [ Star ], rowdefs = [ Star; Auto ]) {
+ VStack() {
+ Label($"Args: {model.Args}")
+ Label($"StepCount from Page B: {model.StepCount}")
+
+ Label($"Count: {model.Count}").centerTextHorizontal()
+
+ Button("Increment", Increment)
+ Button("Decrement", Decrement)
+ }
+
+ (VStack() {
+ Button("Go back", GoBack)
+ Button("Go to Page A", GoToPageA)
+ Button("Go to Page B", GoToPageB)
+ })
+ .gridRow(1)
+ }
+ )
+ .title("Page C")
diff --git a/samples/Navigation/NavigationPath/Platforms/Android/AndroidManifest.xml b/samples/Navigation/NavigationPath/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..bdec9b5
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/NavigationPath/Platforms/Android/MainActivity.fs b/samples/Navigation/NavigationPath/Platforms/Android/MainActivity.fs
new file mode 100644
index 0000000..85f15ab
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Android/MainActivity.fs
@@ -0,0 +1,17 @@
+namespace NavigationSample
+
+open Android.App
+open Android.Content.PM
+open Microsoft.Maui
+
+[]
+type MainActivity() =
+ inherit MauiAppCompatActivity()
diff --git a/samples/Navigation/NavigationPath/Platforms/Android/MainApplication.fs b/samples/Navigation/NavigationPath/Platforms/Android/MainApplication.fs
new file mode 100644
index 0000000..7910a54
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Android/MainApplication.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample
+
+open Android.App
+open Microsoft.Maui
+
+[]
+type MainApplication(handle, ownership) =
+ inherit MauiApplication(handle, ownership)
+
+ override _.CreateMauiApp() = MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/NavigationPath/Platforms/Android/Resources/values/colors.xml b/samples/Navigation/NavigationPath/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..5cd1604
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/samples/Navigation/NavigationPath/Platforms/MacCatalyst/AppDelegate.fs b/samples/Navigation/NavigationPath/Platforms/MacCatalyst/AppDelegate.fs
new file mode 100644
index 0000000..90458e5
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/MacCatalyst/AppDelegate.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample
+
+open Foundation
+open Microsoft.Maui
+
+[]
+type AppDelegate() =
+ inherit MauiUIApplicationDelegate()
+
+ override this.CreateMauiApp() = MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/NavigationPath/Platforms/MacCatalyst/Info.plist b/samples/Navigation/NavigationPath/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000..0690e47
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/samples/Navigation/NavigationPath/Platforms/MacCatalyst/Program.fs b/samples/Navigation/NavigationPath/Platforms/MacCatalyst/Program.fs
new file mode 100644
index 0000000..26c87e7
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/MacCatalyst/Program.fs
@@ -0,0 +1,9 @@
+namespace NavigationSample
+
+open UIKit
+
+module Program =
+ []
+ let main args =
+ UIApplication.Main(args, null, typeof)
+ 0
diff --git a/samples/Navigation/NavigationPath/Platforms/Tizen/Main.fs b/samples/Navigation/NavigationPath/Platforms/Tizen/Main.fs
new file mode 100644
index 0000000..3d59efd
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Tizen/Main.fs
@@ -0,0 +1,16 @@
+namespace NavigationSample
+
+open System
+open Microsoft.Maui
+open Microsoft.Maui.Hosting
+
+type Program() =
+ inherit MauiApplication()
+
+ override this.CreateMauiApp() = MauiProgram.CreateMauiApp()
+
+module Program =
+ []
+ let main args =
+ let app = Program()
+ app.Run(args)
diff --git a/samples/Navigation/NavigationPath/Platforms/Tizen/tizen-manifest.xml b/samples/Navigation/NavigationPath/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 0000000..83e2649
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ appicon.xhigh.png
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/NavigationPath/Platforms/Windows/App.fs b/samples/Navigation/NavigationPath/Platforms/Windows/App.fs
new file mode 100644
index 0000000..ebceb21
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Windows/App.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample.WinUI
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+type App() =
+ inherit FSharp.Maui.WinUICompat.App()
+
+ override this.CreateMauiApp() =
+ NavigationSample.MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/NavigationPath/Platforms/Windows/Main.fs b/samples/Navigation/NavigationPath/Platforms/Windows/Main.fs
new file mode 100644
index 0000000..501c15d
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Windows/Main.fs
@@ -0,0 +1,9 @@
+namespace NavigationSample.WinUI
+
+open System
+
+module Program =
+ []
+ let main args =
+ do FSharp.Maui.WinUICompat.Program.Main(args, typeof)
+ 0
diff --git a/samples/Navigation/NavigationPath/Platforms/Windows/Package.appxmanifest b/samples/Navigation/NavigationPath/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..e98c6ed
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Navigation/NavigationPath/Platforms/Windows/app.manifest b/samples/Navigation/NavigationPath/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..c8a173c
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/samples/Navigation/NavigationPath/Platforms/iOS/AppDelegate.fs b/samples/Navigation/NavigationPath/Platforms/iOS/AppDelegate.fs
new file mode 100644
index 0000000..7418834
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/iOS/AppDelegate.fs
@@ -0,0 +1,10 @@
+namespace NavigationSample
+
+open Foundation
+open Microsoft.Maui
+
+[]
+type AppDelegate() =
+ inherit MauiUIApplicationDelegate()
+
+ override _.CreateMauiApp() = MauiProgram.CreateMauiApp()
diff --git a/samples/Navigation/NavigationPath/Platforms/iOS/Info.plist b/samples/Navigation/NavigationPath/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..358337b
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/samples/Navigation/NavigationPath/Platforms/iOS/Program.fs b/samples/Navigation/NavigationPath/Platforms/iOS/Program.fs
new file mode 100644
index 0000000..26c87e7
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Platforms/iOS/Program.fs
@@ -0,0 +1,9 @@
+namespace NavigationSample
+
+open UIKit
+
+module Program =
+ []
+ let main args =
+ UIApplication.Main(args, null, typeof)
+ 0
diff --git a/samples/Navigation/NavigationPath/Properties/launchSettings.json b/samples/Navigation/NavigationPath/Properties/launchSettings.json
new file mode 100644
index 0000000..c16206a
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/Navigation/NavigationPath/Resources/AppIcon/appicon.svg b/samples/Navigation/NavigationPath/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..5f04fcf
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/samples/Navigation/NavigationPath/Resources/AppIcon/appiconfg.svg b/samples/Navigation/NavigationPath/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/NavigationPath/Resources/Fonts/OpenSans-Regular.ttf b/samples/Navigation/NavigationPath/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..534d009
Binary files /dev/null and b/samples/Navigation/NavigationPath/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/samples/Navigation/NavigationPath/Resources/Fonts/OpenSans-Semibold.ttf b/samples/Navigation/NavigationPath/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..f333153
Binary files /dev/null and b/samples/Navigation/NavigationPath/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/samples/Navigation/NavigationPath/Resources/Images/dotnet_bot.svg b/samples/Navigation/NavigationPath/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000..51b1c33
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/samples/Navigation/NavigationPath/Resources/Raw/AboutAssets.txt b/samples/Navigation/NavigationPath/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..50b8a7b
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/samples/Navigation/NavigationPath/Resources/Splash/splash.svg b/samples/Navigation/NavigationPath/Resources/Splash/splash.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/samples/Navigation/NavigationPath/Sample.fs b/samples/Navigation/NavigationPath/Sample.fs
new file mode 100644
index 0000000..23c4bb6
--- /dev/null
+++ b/samples/Navigation/NavigationPath/Sample.fs
@@ -0,0 +1,77 @@
+namespace NavigationSample
+
+open Fabulous
+open Fabulous.Maui
+
+open type Fabulous.Maui.View
+
+/// This is the root of the app
+module Sample =
+ /// We instantiate a single NavigationController that will be used for the lifetime of the app
+ let nav = NavigationController()
+
+ /// The Model needs only to store the current navigation stack
+ type Model = { Navigation: NavigationStack }
+
+ type Msg =
+ | NavigationPushed of NavigationPath
+ | BackNavigated
+ | NavigationMsg of NavigationState.Msg
+ | BackButtonPressed
+
+ /// This is where we subscribe to the navigation events
+ /// If a navigation forward is requested, we dispatch the NavigationPushed message
+ /// If a navigation backward is requested, we dispatch the BackNavigated message
+ let navSubscription () : Cmd =
+ [ fun dispatch ->
+ nav.Navigated.Add(fun path -> dispatch(NavigationPushed path))
+ nav.BackNavigated.Add(fun () -> dispatch BackNavigated) ]
+
+ /// In the init function, we initialize the NavigationStack and subscribe to the navigation events
+ let init () =
+ let rootPage = NavigationState.init NavigationPath.PageA
+ { Navigation = NavigationStack.Init(rootPage) }, navSubscription()
+
+ let update msg model =
+ match msg with
+ | NavigationPushed path ->
+ // When a new path is pushed, we create a new page and push it on the stack
+ let newPage = NavigationState.init path
+
+ { Navigation = model.Navigation.Push(newPage) }, Cmd.none
+
+ | BackNavigated ->
+ // BackNavigated handles both the back button and the programmatic back navigation
+ // We simply pop the current page from the stack
+ { Navigation = model.Navigation.Pop() }, Cmd.none
+
+ | NavigationMsg navMsg ->
+ let m, navCmd = NavigationState.update nav navMsg model.Navigation.CurrentPage
+
+ { Navigation = model.Navigation.UpdateCurrentPage(m) }, Cmd.map NavigationMsg navCmd
+
+ | BackButtonPressed ->
+ let m, navCmd = NavigationState.updateBackButton nav model.Navigation.CurrentPage
+
+ { Navigation = model.Navigation.UpdateCurrentPage(m) }, Cmd.map NavigationMsg navCmd
+
+ /// The view function contains the NavigationPage control that will display the different pages
+ /// and handle the navigation animations (push, pop) as well has displaying a back button by default
+ ///
+ /// Because of MVU, all the pages need to return the same Msg type but they all have their own.
+ /// To be able to wrap those Msgs into the app's root Msg type, we use the View.map helper function.
+ let view model =
+ Application(
+ (NavigationPage() {
+ // We inject in the NavigationPage history the back stack of our navigation
+ for navModel in List.rev model.Navigation.BackStack do
+ (View.map NavigationMsg (NavigationState.view navModel)).hasBackButton(false)
+
+ // The page currently displayed is the one on top of the stack
+ (View.map NavigationMsg (NavigationState.view model.Navigation.CurrentPage))
+ .hasBackButton(false)
+ })
+ .onBackButtonPressed(BackButtonPressed)
+ )
+
+ let program = Program.statefulWithCmd init update |> Program.withView view