diff --git a/.gitignore b/.gitignore index 3e759b7..458a7ec 100644 --- a/.gitignore +++ b/.gitignore @@ -328,3 +328,5 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ + +.idea/ diff --git a/TemplateBundle/content/BasicTemplate/BasicTemplate.fsproj b/TemplateBundle/content/BasicTemplate/BasicTemplate.fsproj index d053371..3ee11b4 100644 --- a/TemplateBundle/content/BasicTemplate/BasicTemplate.fsproj +++ b/TemplateBundle/content/BasicTemplate/BasicTemplate.fsproj @@ -13,10 +13,10 @@ - - - - + + + + diff --git a/TemplateBundle/content/BasicTemplate/Counter.fs b/TemplateBundle/content/BasicTemplate/Counter.fs index 95fe7bf..ef79d82 100644 --- a/TemplateBundle/content/BasicTemplate/Counter.fs +++ b/TemplateBundle/content/BasicTemplate/Counter.fs @@ -4,18 +4,21 @@ module Counter = open Avalonia.Controls open Avalonia.FuncUI.DSL open Avalonia.Layout - - type State = { count : int } + + type State = { count: int } let init = { count = 0 } - type Msg = Increment | Decrement | Reset + type Msg = + | Increment + | Decrement + | Reset - let update (msg: Msg) (state: State) : State = + let update (msg: Msg) (state: State): State = match msg with | Increment -> { state with count = state.count + 1 } | Decrement -> { state with count = state.count - 1 } | Reset -> init - + let view (state: State) (dispatch) = DockPanel.create [ DockPanel.children [ @@ -23,7 +26,7 @@ module Counter = Button.dock Dock.Bottom Button.onClick (fun _ -> dispatch Reset) Button.content "reset" - ] + ] Button.create [ Button.dock Dock.Bottom Button.onClick (fun _ -> dispatch Decrement) @@ -42,4 +45,4 @@ module Counter = TextBlock.text (string state.count) ] ] - ] \ No newline at end of file + ] diff --git a/TemplateBundle/content/BasicTemplate/Program.fs b/TemplateBundle/content/BasicTemplate/Program.fs index a7084d2..f25fdc7 100644 --- a/TemplateBundle/content/BasicTemplate/Program.fs +++ b/TemplateBundle/content/BasicTemplate/Program.fs @@ -10,45 +10,47 @@ open Avalonia.FuncUI.Components.Hosts type MainWindow() as this = inherit HostWindow() + do base.Title <- "BasicTemplate" base.Width <- 400.0 base.Height <- 400.0 - + //this.VisualRoot.VisualRoot.Renderer.DrawFps <- true //this.VisualRoot.VisualRoot.Renderer.DrawDirtyRects <- true - +//-:cnd:noEmit #if DEBUG this.AttachDevTools(KeyGesture(Key.F12)) #endif +//+:cnd:noEmit Elmish.Program.mkSimple (fun () -> Counter.init) Counter.update Counter.view |> Program.withHost this +//-:cnd:noEmit #if DEBUG |> Program.withConsoleTrace #endif +//+:cnd:noEmit |> Program.run - + type App() = inherit Application() override this.Initialize() = - this.Styles.Load "avares://Avalonia.Themes.Default/DefaultTheme.xaml" - this.Styles.Load "avares://Avalonia.Themes.Default/Accents/BaseDark.xaml" + this.Styles.Load "avares://Avalonia.Themes.Fluent/FluentDark.xaml" override this.OnFrameworkInitializationCompleted() = match this.ApplicationLifetime with - | :? IClassicDesktopStyleApplicationLifetime as desktopLifetime -> - desktopLifetime.MainWindow <- MainWindow() + | :? IClassicDesktopStyleApplicationLifetime as desktopLifetime -> desktopLifetime.MainWindow <- MainWindow() | _ -> () module Program = [] - let main(args: string[]) = + let main (args: string []) = AppBuilder .Configure() .UsePlatformDetect() .UseSkia() - .StartWithClassicDesktopLifetime(args) \ No newline at end of file + .StartWithClassicDesktopLifetime(args) diff --git a/TemplateBundle/content/FullTemplate/About.fs b/TemplateBundle/content/FullTemplate/About.fs index d12fe0d..6368070 100644 --- a/TemplateBundle/content/FullTemplate/About.fs +++ b/TemplateBundle/content/FullTemplate/About.fs @@ -13,8 +13,7 @@ module About = open Avalonia.FuncUI.DSL - type State = - { noop: bool } + type State = { noop: bool } type Links = | AvaloniaRepository @@ -33,9 +32,9 @@ module About = let update (msg: Msg) (state: State) = match msg with - | OpenUrl link -> - let url = - match link with + | OpenUrl link -> + let url = + match link with | AvaloniaRepository -> "https://github.com/AvaloniaUI/Avalonia" | AvaloniaAwesome -> "https://github.com/AvaloniaCommunity/awesome-avalonia" | AvaloniaGitter -> "https://gitter.im/AvaloniaUI" @@ -44,17 +43,21 @@ module About = | FuncUIGitter -> "https://gitter.im/Avalonia-FuncUI" | FuncUINetTemplates -> "https://github.com/AvaloniaCommunity/Avalonia.FuncUI.ProjectTemplates" | FuncUISamples -> "https://github.com/AvaloniaCommunity/Avalonia.FuncUI/tree/master/src/Examples" - + if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then let start = sprintf "/c start %s" url - Process.Start(ProcessStartInfo("cmd", start)) |> ignore + + Process.Start(ProcessStartInfo("cmd", start)) + |> ignore else if RuntimeInformation.IsOSPlatform(OSPlatform.Linux) then Process.Start("xdg-open", url) |> ignore else if RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then Process.Start("open", url) |> ignore + + state, Cmd.none - let headerView (dock: Dock): IView = + let headerView (dock: Dock): IView = StackPanel.create [ StackPanel.dock dock StackPanel.verticalAlignment VerticalAlignment.Top @@ -65,17 +68,17 @@ module About = ] TextBlock.create [ TextBlock.classes [ "subtitle" ] - TextBlock.text ( - "Avalonia.FuncUI is a project that provides you with an Elmish DSL for Avalonia Controls\n" + - "for you to use in an F# idiomatic way. We hope you like the project and spread the word :)\n" + - "Questions ? Reach to us on Gitter, also check the links below" - ) + TextBlock.text + ("Avalonia.FuncUI is a project that provides you with an Elmish DSL for Avalonia Controls\n" + + "for you to use in an F# idiomatic way. We hope you like the project and spread the word :)\n" + + "Questions ? Reach to us on Gitter, also check the links below") ] ] - ] |> Helpers.generalize - - - let avaloniaLinksView (dock: Dock) (dispatch: Msg -> unit) : IView = + ] + |> Helpers.generalize + + + let avaloniaLinksView (dock: Dock) (dispatch: Msg -> unit): IView = StackPanel.create [ StackPanel.dock dock StackPanel.horizontalAlignment HorizontalAlignment.Left @@ -86,28 +89,29 @@ module About = ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaRepository)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaRepository)) TextBlock.text "Avalonia Repository" ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaAwesome)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaAwesome)) TextBlock.text "Awesome Avalonia" ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaGitter)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaGitter)) TextBlock.text "Gitter" ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaCommunity)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaCommunity)) TextBlock.text "Avalonia Community" ] ] - ] |> Helpers.generalize - - let avaloniaFuncUILinksView (dock: Dock) (dispatch: Msg -> unit) : IView = + ] + |> Helpers.generalize + + let avaloniaFuncUILinksView (dock: Dock) (dispatch: Msg -> unit): IView = StackPanel.create [ StackPanel.dock dock StackPanel.horizontalAlignment HorizontalAlignment.Right @@ -118,27 +122,28 @@ module About = ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUIRepository)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUIRepository)) TextBlock.text "Avalonia.FuncUI Repository" ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUIGitter)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUIGitter)) TextBlock.text "Gitter" ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUINetTemplates)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUINetTemplates)) TextBlock.text ".Net Templates" ] TextBlock.create [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUISamples)) + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUISamples)) TextBlock.text "Samples" - ] + ] ] - ] |> Helpers.generalize - + ] + |> Helpers.generalize + let view (state: State) (dispatch: Msg -> unit) = DockPanel.create [ DockPanel.horizontalAlignment HorizontalAlignment.Center @@ -149,4 +154,4 @@ module About = avaloniaLinksView Dock.Left dispatch avaloniaFuncUILinksView Dock.Right dispatch ] - ] \ No newline at end of file + ] diff --git a/TemplateBundle/content/FullTemplate/Counter.fs b/TemplateBundle/content/FullTemplate/Counter.fs index f44135a..0df5366 100644 --- a/TemplateBundle/content/FullTemplate/Counter.fs +++ b/TemplateBundle/content/FullTemplate/Counter.fs @@ -4,18 +4,21 @@ module Counter = open Avalonia.Controls open Avalonia.FuncUI.DSL open Avalonia.Layout - - type State = { count : int } + + type State = { count: int } let init = { count = 0 } - type Msg = Increment | Decrement | Reset + type Msg = + | Increment + | Decrement + | Reset - let update (msg: Msg) (state: State) : State = + let update (msg: Msg) (state: State): State = match msg with | Increment -> { state with count = state.count + 1 } | Decrement -> { state with count = state.count - 1 } | Reset -> init - + let view (state: State) (dispatch) = DockPanel.create [ DockPanel.children [ @@ -37,7 +40,7 @@ module Counter = Button.create [ Button.onClick (fun _ -> dispatch Reset) Button.content "reset" - ] + ] ] ] @@ -49,4 +52,4 @@ module Counter = TextBlock.text (string state.count) ] ] - ] \ No newline at end of file + ] diff --git a/TemplateBundle/content/FullTemplate/FullTemplate.fsproj b/TemplateBundle/content/FullTemplate/FullTemplate.fsproj index f10f466..b65b455 100644 --- a/TemplateBundle/content/FullTemplate/FullTemplate.fsproj +++ b/TemplateBundle/content/FullTemplate/FullTemplate.fsproj @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/TemplateBundle/content/FullTemplate/Program.fs b/TemplateBundle/content/FullTemplate/Program.fs index f9e3bf9..42a6357 100644 --- a/TemplateBundle/content/FullTemplate/Program.fs +++ b/TemplateBundle/content/FullTemplate/Program.fs @@ -24,7 +24,8 @@ module Program = [] let main (args: string []) = - AppBuilder.Configure() + AppBuilder + .Configure() .UsePlatformDetect() .UseSkia() .StartWithClassicDesktopLifetime(args) diff --git a/TemplateBundle/content/FullTemplate/Shell.fs b/TemplateBundle/content/FullTemplate/Shell.fs index 8febee9..0bde718 100644 --- a/TemplateBundle/content/FullTemplate/Shell.fs +++ b/TemplateBundle/content/FullTemplate/Shell.fs @@ -18,7 +18,8 @@ module Shell = type State = /// store the child state in your main state - { aboutState: About.State; counterState: Counter.State;} + { aboutState: About.State + counterState: Counter.State } type Msg = | AboutMsg of About.Msg @@ -27,7 +28,9 @@ module Shell = let init = let aboutState, aboutCmd = About.init let counterState = Counter.init - { aboutState = aboutState; counterState = counterState }, + + { aboutState = aboutState + counterState = counterState }, /// If your children controls don't emit any commands /// in the init function, you can just return Cmd.none /// otherwise, you can use a batch operation on all of them @@ -37,32 +40,39 @@ module Shell = let update (msg: Msg) (state: State): State * Cmd<_> = match msg with | AboutMsg bpmsg -> - let aboutState, cmd = - About.update bpmsg state.aboutState + let aboutState, cmd = About.update bpmsg state.aboutState + { state with aboutState = aboutState }, - /// map the message to the kind of message + /// map the message to the kind of message /// your child control needs to handle Cmd.map AboutMsg cmd | CounterMsg countermsg -> let counterMsg = Counter.update countermsg state.counterState + { state with counterState = counterMsg }, - /// map the message to the kind of message + /// map the message to the kind of message /// your child control needs to handle Cmd.none let view (state: State) (dispatch) = - DockPanel.create - [ DockPanel.children - [ TabControl.create - [ TabControl.tabStripPlacement Dock.Top - TabControl.viewItems - [ TabItem.create - [ TabItem.header "Counter Sample" - TabItem.content (Counter.view state.counterState (CounterMsg >> dispatch)) ] - TabItem.create - [ TabItem.header "About" - TabItem.content (About.view state.aboutState (AboutMsg >> dispatch)) ] ] ] ] ] + DockPanel.create [ + DockPanel.children [ + TabControl.create [ + TabControl.tabStripPlacement Dock.Top + TabControl.viewItems [ + TabItem.create [ + TabItem.header "Counter Sample" + TabItem.content (Counter.view state.counterState (CounterMsg >> dispatch)) + ] + TabItem.create [ + TabItem.header "About" + TabItem.content (About.view state.aboutState (AboutMsg >> dispatch)) + ] + ] + ] + ] + ] /// This is the main window of your application /// you can do all sort of useful things here like setting heights and widths @@ -70,6 +80,7 @@ module Shell = /// Avalonia type MainWindow() as this = inherit HostWindow() + do base.Title <- "Full App" base.Width <- 800.0 @@ -79,13 +90,18 @@ module Shell = //this.VisualRoot.VisualRoot.Renderer.DrawFps <- true //this.VisualRoot.VisualRoot.Renderer.DrawDirtyRects <- true +//-:cnd:noEmit #if DEBUG this.AttachDevTools(KeyGesture(Key.F12)) #endif +//+:cnd:noEmit + Elmish.Program.mkProgram (fun () -> init) update view |> Program.withHost this +//-:cnd:noEmit #if DEBUG |> Program.withConsoleTrace #endif +//+:cnd:noEmit |> Program.run diff --git a/TemplateBundle/content/FullTemplate/Styles.xaml b/TemplateBundle/content/FullTemplate/Styles.xaml index f85c78a..0b16e78 100644 --- a/TemplateBundle/content/FullTemplate/Styles.xaml +++ b/TemplateBundle/content/FullTemplate/Styles.xaml @@ -1,7 +1,7 @@ - + @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/QuickStart.Core.Tests.fsproj b/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/QuickStart.Core.Tests.fsproj index a66d4ad..0dfe537 100644 --- a/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/QuickStart.Core.Tests.fsproj +++ b/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/QuickStart.Core.Tests.fsproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 false diff --git a/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/Sample.fs b/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/Sample.fs index 3f97524..871e010 100644 --- a/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/Sample.fs +++ b/TemplateBundle/content/QuickStart/QuickStart.Core.Tests/Sample.fs @@ -8,24 +8,29 @@ module Sample = [] let tests = - testList "samples" + testList + "samples" [ test "Hello, World!" { /// Testing our Core Library let actual = hello "World!" Expect.equal actual "Hello, World!" "hello Should say Hello, World!" } /// Expecto Tests sample https://github.com/haf/expecto#expecto - testCase "universe exists (╭ರᴥ•́)" <| fun _ -> + testCase "universe exists (╭ರᴥ•́)" + <| fun _ -> let subject = true Expect.isTrue subject "I compute, therefore I am." - testCase "when true is not (should fail)" <| fun _ -> + testCase "when true is not (should fail)" + <| fun _ -> let subject = false Expect.isTrue subject "I should fail because the subject is false" - testCase "I'm skipped (should skip)" <| fun _ -> Tests.skiptest "Yup, waiting for a sunny day..." + testCase "I'm skipped (should skip)" + <| fun _ -> Tests.skiptest "Yup, waiting for a sunny day..." - testCase "I'm always fail (should fail)" <| fun _ -> Tests.failtest "This was expected..." + testCase "I'm always fail (should fail)" + <| fun _ -> Tests.failtest "This was expected..." testCase "contains things" <| fun _ -> Expect.containsAll [| 2; 3; 4 |] [| 2; 4 |] "This is the case; {2,3,4} contains {2,4}" diff --git a/TemplateBundle/content/QuickStart/QuickStart.Core/Users.fs b/TemplateBundle/content/QuickStart/QuickStart.Core/Users.fs index 0a98d67..e338a45 100644 --- a/TemplateBundle/content/QuickStart/QuickStart.Core/Users.fs +++ b/TemplateBundle/content/QuickStart/QuickStart.Core/Users.fs @@ -3,8 +3,8 @@ namespace QuickStart.Core open System.Net.Http open System.IO -/// This is a sample module that grabs users from -/// https://randomuser.me/ and also showcases how can you leverage +/// This is a sample module that grabs users from +/// https://randomuser.me/ and also showcases how can you leverage /// F# TypeProviders in an idiomatic way that fits F# style /// Use modules like this to abstract logic and allow yourself to re-use /// code among projects (other apps, or server side logic) @@ -15,8 +15,11 @@ module Users = let getUsers (page: int option) (limit: int option) = let page = defaultArg page 1 let limit = defaultArg limit 10 + async { - let url = sprintf "%s?page=%i&results=%i" baseurl page limit + let url = + sprintf "%s?page=%i&results=%i" baseurl page limit + let! sample = UserEndpoint.AsyncLoad(url) return sample.Results } @@ -37,5 +40,4 @@ module Users = return user, name } - let getFulNameStr (name: UserEndpoint.Name) = - sprintf "%s %s" name.First name.Last + let getFulNameStr (name: UserEndpoint.Name) = sprintf "%s %s" name.First name.Last diff --git a/TemplateBundle/content/QuickStart/QuickStart/About.fs b/TemplateBundle/content/QuickStart/QuickStart/About.fs index d477892..e45c12a 100644 --- a/TemplateBundle/content/QuickStart/QuickStart/About.fs +++ b/TemplateBundle/content/QuickStart/QuickStart/About.fs @@ -11,8 +11,7 @@ module About = open Avalonia.FuncUI.DSL - type State = - { noop: bool } + type State = { noop: bool } type Links = | AvaloniaRepository @@ -31,9 +30,9 @@ module About = let update (msg: Msg) (state: State) = match msg with - | OpenUrl link -> - let url = - match link with + | OpenUrl link -> + let url = + match link with | AvaloniaRepository -> "https://github.com/AvaloniaUI/Avalonia" | AvaloniaAwesome -> "https://github.com/AvaloniaCommunity/awesome-avalonia" | AvaloniaGitter -> "https://gitter.im/AvaloniaUI" @@ -42,77 +41,102 @@ module About = | FuncUIGitter -> "https://gitter.im/Avalonia-FuncUI" | FuncUINetTemplates -> "https://github.com/AvaloniaCommunity/Avalonia.FuncUI.ProjectTemplates" | FuncUISamples -> "https://github.com/AvaloniaCommunity/Avalonia.FuncUI/tree/master/src/Examples" - + if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then let start = sprintf "/c start %s" url - Process.Start(ProcessStartInfo("cmd", start)) |> ignore + + Process.Start(ProcessStartInfo("cmd", start)) + |> ignore else if RuntimeInformation.IsOSPlatform(OSPlatform.Linux) then Process.Start("xdg-open", url) |> ignore else if RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then Process.Start("open", url) |> ignore + + + state, Cmd.none let view (state: State) (dispatch: Msg -> unit) = - DockPanel.create - [ DockPanel.horizontalAlignment HorizontalAlignment.Center - DockPanel.verticalAlignment VerticalAlignment.Top - DockPanel.children - [ StackPanel.create - [ StackPanel.dock Dock.Top - StackPanel.verticalAlignment VerticalAlignment.Top - StackPanel.children - [ TextBlock.create - [ TextBlock.classes [ "title" ] - TextBlock.text "Thank you for using Avalonia.FuncUI" ] - TextBlock.create - [ TextBlock.classes [ "subtitle" ] - TextBlock.text - ("Avalonia.FuncUI is a project that provides you with an Elmish DSL for Avalonia Controls\n" + - "for you to use in an F# idiomatic way. We hope you like the project and spread the word :)\n" + - "Do Have questions? Reach to us on gitter, also check the links below") ] ] ] - StackPanel.create - [ StackPanel.dock Dock.Left - StackPanel.horizontalAlignment HorizontalAlignment.Left - StackPanel.children - [ TextBlock.create - [ TextBlock.classes [ "title" ] - TextBlock.text "Avalonia" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaRepository)) - TextBlock.text "Avalonia Repository" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaAwesome)) - TextBlock.text "Awesome Avalonia" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaGitter)) - TextBlock.text "Gitter" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl AvaloniaCommunity)) - TextBlock.text "Avalonia Community" ] ] ] - StackPanel.create - [ StackPanel.dock Dock.Right - StackPanel.horizontalAlignment HorizontalAlignment.Right - StackPanel.children - [ TextBlock.create - [ TextBlock.classes [ "title" ] - TextBlock.text "Avalonia.FuncUI" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUIRepository)) - TextBlock.text "Avalonia.FuncUI Repository" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUIGitter)) - TextBlock.text "Gitter" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUINetTemplates)) - TextBlock.text ".Net Templates" ] - TextBlock.create - [ TextBlock.classes [ "link" ] - TextBlock.onTapped(fun _ -> dispatch (OpenUrl FuncUISamples)) - TextBlock.text "Samples" ] ] ] ] ] + DockPanel.create [ + DockPanel.horizontalAlignment HorizontalAlignment.Center + DockPanel.verticalAlignment VerticalAlignment.Top + DockPanel.children [ + StackPanel.create [ + StackPanel.dock Dock.Top + StackPanel.verticalAlignment VerticalAlignment.Top + StackPanel.children [ + TextBlock.create [ + TextBlock.classes [ "title" ] + TextBlock.text "Thank you for using Avalonia.FuncUI" + ] + TextBlock.create [ + TextBlock.classes [ "subtitle" ] + TextBlock.text + ("Avalonia.FuncUI is a project that provides you with an Elmish DSL for Avalonia Controls\n" + + "for you to use in an F# idiomatic way. We hope you like the project and spread the word :)\n" + + "Do Have questions? Reach to us on gitter, also check the links below") + ] + ] + ] + StackPanel.create [ + StackPanel.dock Dock.Left + StackPanel.horizontalAlignment HorizontalAlignment.Left + StackPanel.children [ + TextBlock.create [ + TextBlock.classes [ "title" ] + TextBlock.text "Avalonia" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaRepository)) + TextBlock.text "Avalonia Repository" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaAwesome)) + TextBlock.text "Awesome Avalonia" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaGitter)) + TextBlock.text "Gitter" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl AvaloniaCommunity)) + TextBlock.text "Avalonia Community" + ] + ] + ] + StackPanel.create [ + StackPanel.dock Dock.Right + StackPanel.horizontalAlignment HorizontalAlignment.Right + StackPanel.children [ + TextBlock.create [ + TextBlock.classes [ "title" ] + TextBlock.text "Avalonia.FuncUI" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUIRepository)) + TextBlock.text "Avalonia.FuncUI Repository" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUIGitter)) + TextBlock.text "Gitter" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUINetTemplates)) + TextBlock.text ".Net Templates" + ] + TextBlock.create [ + TextBlock.classes [ "link" ] + TextBlock.onTapped (fun _ -> dispatch (OpenUrl FuncUISamples)) + TextBlock.text "Samples" + ] + ] + ] + ] + ] diff --git a/TemplateBundle/content/QuickStart/QuickStart/Program.fs b/TemplateBundle/content/QuickStart/QuickStart/Program.fs index 712957e..28b3fcd 100644 --- a/TemplateBundle/content/QuickStart/QuickStart/Program.fs +++ b/TemplateBundle/content/QuickStart/QuickStart/Program.fs @@ -24,7 +24,8 @@ module Program = [] let main (args: string []) = - AppBuilder.Configure() + AppBuilder + .Configure() .UsePlatformDetect() .UseSkia() .StartWithClassicDesktopLifetime(args) diff --git a/TemplateBundle/content/QuickStart/QuickStart/QuickStart.fsproj b/TemplateBundle/content/QuickStart/QuickStart/QuickStart.fsproj index 69928ba..20ddd0f 100644 --- a/TemplateBundle/content/QuickStart/QuickStart/QuickStart.fsproj +++ b/TemplateBundle/content/QuickStart/QuickStart/QuickStart.fsproj @@ -15,10 +15,10 @@ - - - - + + + + diff --git a/TemplateBundle/content/QuickStart/QuickStart/Shell.fs b/TemplateBundle/content/QuickStart/QuickStart/Shell.fs index 182e651..7ffa4ce 100644 --- a/TemplateBundle/content/QuickStart/QuickStart/Shell.fs +++ b/TemplateBundle/content/QuickStart/QuickStart/Shell.fs @@ -16,14 +16,13 @@ module Shell = open Avalonia.FuncUI.DSL open Avalonia.FuncUI.Elmish - type State = - { aboutState: About.State } + type State = { aboutState: About.State } - type Msg = - | AboutMsg of About.Msg + type Msg = AboutMsg of About.Msg let init = let aboutState, bpCmd = About.init + { aboutState = aboutState }, /// If your children controls don't emit any commands /// in the init function, you can just return Cmd.none @@ -34,32 +33,39 @@ module Shell = let update (msg: Msg) (state: State): State * Cmd<_> = match msg with | AboutMsg bpmsg -> - let aboutState, cmd = - About.update bpmsg state.aboutState + let aboutState, cmd = About.update bpmsg state.aboutState + { state with aboutState = aboutState }, - /// map the message to the kind of message + /// map the message to the kind of message /// your child control needs to handle Cmd.map AboutMsg cmd let view (state: State) (dispatch) = - DockPanel.create - [ DockPanel.children - [ TabControl.create - [ TabControl.tabStripPlacement Dock.Top - TabControl.viewItems - [ TabItem.create - [ TabItem.header "TreeView Page" - /// If you don't need to be aware of the child control's state - /// you can use the ViewBuilder to create the Host element and render it - TabItem.content (ViewBuilder.Create([])) ] - TabItem.create - [ TabItem.header "User Profiles Page" - TabItem.content (ViewBuilder.Create([])) ] - TabItem.create - [ TabItem.header "About" - /// Use your child control's view function to render it, also don't forget to compose - /// your dispatch function so it can handle the child control's message - TabItem.content (About.view state.aboutState (AboutMsg >> dispatch)) ] ] ] ] ] + DockPanel.create [ + DockPanel.children [ + TabControl.create [ + TabControl.tabStripPlacement Dock.Top + TabControl.viewItems [ + TabItem.create [ + TabItem.header "TreeView Page" + /// If you don't need to be aware of the child control's state + /// you can use the ViewBuilder to create the Host element and render it + TabItem.content (ViewBuilder.Create([])) + ] + TabItem.create [ + TabItem.header "User Profiles Page" + TabItem.content (ViewBuilder.Create([])) + ] + TabItem.create [ + TabItem.header "About" + /// Use your child control's view function to render it, also don't forget to compose + /// your dispatch function so it can handle the child control's message + TabItem.content (About.view state.aboutState (AboutMsg >> dispatch)) + ] + ] + ] + ] + ] /// This is the main window of your application /// you can do all sort of useful things here like setting heights and widths @@ -67,6 +73,7 @@ module Shell = /// Avalonia type MainWindow() as this = inherit HostWindow() + do base.Title <- "Quickstart" base.Width <- 800.0 @@ -76,13 +83,16 @@ module Shell = //this.VisualRoot.VisualRoot.Renderer.DrawFps <- true //this.VisualRoot.VisualRoot.Renderer.DrawDirtyRects <- true +//-:cnd:noEmit #if DEBUG this.AttachDevTools(KeyGesture(Key.F12)) #endif - +//+:cnd:noEmit Elmish.Program.mkProgram (fun () -> init) update view |> Program.withHost this +//-:cnd:noEmit #if DEBUG |> Program.withConsoleTrace #endif +//+:cnd:noEmit |> Program.run diff --git a/TemplateBundle/content/QuickStart/QuickStart/Styles.xaml b/TemplateBundle/content/QuickStart/QuickStart/Styles.xaml index 0890604..15cc3da 100644 --- a/TemplateBundle/content/QuickStart/QuickStart/Styles.xaml +++ b/TemplateBundle/content/QuickStart/QuickStart/Styles.xaml @@ -1,7 +1,7 @@ - + @@ -37,4 +37,4 @@ - \ No newline at end of file + diff --git a/TemplateBundle/content/QuickStart/QuickStart/TreeViewPage.fs b/TemplateBundle/content/QuickStart/QuickStart/TreeViewPage.fs index f56556f..0843ed9 100644 --- a/TemplateBundle/content/QuickStart/QuickStart/TreeViewPage.fs +++ b/TemplateBundle/content/QuickStart/QuickStart/TreeViewPage.fs @@ -21,19 +21,14 @@ module TreeViewPage = Children = [ { Name = "Fruit" Children = - [ { Name = "Tomato" - Children = [] } - { Name = "Apple" - Children = [] } ] } + [ { Name = "Tomato"; Children = [] } + { Name = "Apple"; Children = [] } ] } { Name = "Vegetables" Children = - [ { Name = "Carrot" - Children = [] } - { Name = "Salad" - Children = [] } ] } ] } + [ { Name = "Carrot"; Children = [] } + { Name = "Salad"; Children = [] } ] } ] } - type State = - { detail: Taxonomy option } + type State = { detail: Taxonomy option } let init = { detail = None } @@ -44,53 +39,68 @@ module TreeViewPage = | ShowDetail taxonomy -> { state with detail = Some taxonomy } let view (state: State) (dispatch: Msg -> unit) = - DockPanel.create - [ DockPanel.children - [ yield TreeView.create - [ TreeView.dock Dock.Left - /// dataItems refers to the source of your control's data - /// these are going to be iterated to fill your template's contents - TreeView.dataItems [ food ] - TreeView.itemTemplate - /// You can pass the type of your data collection - /// to have a safe type reference in the create function - (DataTemplateView - .create - ((fun data -> data.Children), - (fun data -> - TextBlock.create - [ TextBlock.onTapped (fun _ -> dispatch (ShowDetail data)) - TextBlock.text data.Name ]))) ] - /// Use Pattern Matching to decide what you want to show - /// based on your state's content - match state.detail with - | Some taxonomy -> - yield StackPanel.create - [ StackPanel.horizontalAlignment HorizontalAlignment.Center - StackPanel.spacing 8.0 - StackPanel.children - [ TextBlock.create [ TextBlock.text (sprintf "Name: %s" taxonomy.Name) ] - StackPanel.create - [ StackPanel.spacing 8.0 - StackPanel.orientation Orientation.Horizontal - StackPanel.children - [ yield TextBlock.create [ TextBlock.text "Children: " ] - for child in taxonomy.Children do - yield TextBlock.create [ TextBlock.text child.Name ] ] ] ] ] - | None -> - yield TextBlock.create - [ StackPanel.horizontalAlignment HorizontalAlignment.Center - TextBlock.text "Select a taxonomy" ] ] ] + DockPanel.create [ + DockPanel.children [ + TreeView.create [ + TreeView.dock Dock.Left + /// dataItems refers to the source of your control's data + /// these are going to be iterated to fill your template's contents + TreeView.dataItems [ food ] + TreeView.itemTemplate + /// You can pass the type of your data collection + /// to have a safe type reference in the create function + (DataTemplateView + .create((fun data -> data.Children), + (fun data -> + TextBlock.create [ + TextBlock.onTapped (fun _ -> dispatch (ShowDetail data)) + TextBlock.text data.Name + ]))) + ] + /// Use Pattern Matching to decide what you want to show + /// based on your state's content + match state.detail with + | Some taxonomy -> + StackPanel.create [ + StackPanel.horizontalAlignment HorizontalAlignment.Center + StackPanel.spacing 8.0 + StackPanel.children [ + TextBlock.create [ + TextBlock.text (sprintf "Name: %s" taxonomy.Name) + ] + StackPanel.create [ + StackPanel.spacing 8.0 + StackPanel.orientation Orientation.Horizontal + StackPanel.children [ + TextBlock.create [ + TextBlock.text "Children: " + ] + for child in taxonomy.Children do + TextBlock.create [ + TextBlock.text child.Name + ] + ] + ] + ] + ] + | None -> + TextBlock.create [ + StackPanel.horizontalAlignment HorizontalAlignment.Center + TextBlock.text "Select a taxonomy" + ] + ] + ] type Host() as this = inherit Hosts.HostControl() + do /// You can use `.mkProgram` to pass Commands around /// if you decide to use it, you have to also return a Command in the initFn /// (init, Cmd.none) /// you can learn more at https://elmish.github.io/elmish/basics.html - let startFn () = - init + let startFn () = init + Elmish.Program.mkSimple startFn update view |> Program.withHost this #if DEBUG diff --git a/TemplateBundle/content/QuickStart/QuickStart/UserProfiles.fs b/TemplateBundle/content/QuickStart/QuickStart/UserProfiles.fs index 10cdc04..29b2330 100644 --- a/TemplateBundle/content/QuickStart/QuickStart/UserProfiles.fs +++ b/TemplateBundle/content/QuickStart/QuickStart/UserProfiles.fs @@ -14,8 +14,7 @@ module UserProfiles = open QuickStart.Core /// sample function to load the initial data - let loadInit() = - Users.getUsers None None + let loadInit () = Users.getUsers None None type State = @@ -27,67 +26,82 @@ module UserProfiles = /// you can dispatch commands in your init if you chose to use `Program.mkProgram` /// instead of `Program.mkSimple` - let init = { users = Array.empty }, Cmd.OfAsync.perform loadInit () LoadImages + let init = + { users = Array.empty }, Cmd.OfAsync.perform loadInit () LoadImages let update (msg: Msg) (state: State): State * Cmd<_> = match msg with | LoadImages users -> - let loadingImgs() = + let loadingImgs () = async { - let! requests = users - |> Array.map (fun user -> Users.getImageFromUrl user user.Picture.Large) - |> Async.Parallel - return requests |> Array.Parallel.map (fun (user, src) -> user, new Bitmap(src)) + let! requests = + users + |> Array.map (fun user -> Users.getImageFromUrl user user.Picture.Large) + |> Async.Parallel + + return + requests + |> Array.Parallel.map (fun (user, src) -> user, new Bitmap(src)) } /// if you need to do asynchronous requests - /// F# and Elmish provide nice constructs like in this case async {} blocks + /// F# and Elmish provide nice constructs like in this case async {} blocks /// and Cmd.OfAsync module. Learn more at /// https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows /// https://elmish.github.io/elmish/cmd.html state, Cmd.OfAsync.perform loadingImgs () SetUsers - | SetUsers users -> - { state with users = users }, Cmd.none + | SetUsers users -> { state with users = users }, Cmd.none let private userProfile (user: Users.UserEndpoint.Result, img: Bitmap) = - WrapPanel.create - [ WrapPanel.classes [ "userprofile" ] - WrapPanel.children - [ Image.create - [ Image.classes [ "profileimg" ] - Image.source img ] - DockPanel.create - [ DockPanel.classes [ "profilepanel" ] - DockPanel.children - [ TextBlock.create - [ TextBlock.classes [ "profiletitle" ] - TextBlock.dock Dock.Top - TextBlock.text user.Name.Title ] - TextBlock.create - [ TextBlock.classes [ "profilename" ] - TextBlock.dock Dock.Top - TextBlock.text (Users.getFulNameStr user.Name) ] - TextBlock.create - [ TextBlock.classes [ "profileemail" ] - TextBlock.dock Dock.Top - TextBlock.text user.Email ] ] ] ] ] + WrapPanel.create [ + WrapPanel.classes [ "userprofile" ] + WrapPanel.children [ + Image.create [ + Image.classes [ "profileimg" ] + Image.source img + ] + DockPanel.create [ + DockPanel.classes [ "profilepanel" ] + DockPanel.children [ + TextBlock.create [ + TextBlock.classes [ "profiletitle" ] + TextBlock.dock Dock.Top + TextBlock.text user.Name.Title + ] + TextBlock.create [ + TextBlock.classes [ "profilename" ] + TextBlock.dock Dock.Top + TextBlock.text (Users.getFulNameStr user.Name) + ] + TextBlock.create [ + TextBlock.classes [ "profileemail" ] + TextBlock.dock Dock.Top + TextBlock.text user.Email + ] + ] + ] + ] + ] let view (state: State) (dispatch: Msg -> unit) = - WrapPanel.create - [ WrapPanel.classes [ "userprofilesgrid" ] - WrapPanel.children - [ for user in state.users do - yield userProfile user ] ] + WrapPanel.create [ + WrapPanel.classes [ "userprofilesgrid" ] + WrapPanel.children [ + for user in state.users do + userProfile user + ] + ] type Host() as this = inherit Hosts.HostControl() + do /// You can use `.mkSimple` yo remove the need of passing a command /// if you choose to do so, you need to remove command from the tuple on your init fn /// you can learn more at https://elmish.github.io/elmish/basics.html - let startFn () = - init + let startFn () = init + Elmish.Program.mkProgram startFn update view |> Program.withHost this #if DEBUG