-
-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interactive use with FSI? #147
Comments
Hi @EHotwagner this is an interesting question I don't have any idea right now but the general gist is that FuncUI is a DSL over Avalonia controls, so the question would be more about How to make Avalonia run in an F# script? I just tried something but I get this error here's the script #r "nuget: Avalonia.Desktop, 0.9.11"
#r "nuget: JaggerJo.Avalonia.FuncUI, 0.4.1"
#r "nuget: JaggerJo.Avalonia.FuncUI.DSL, 0.4.3"
#r "nuget: JaggerJo.Avalonia.FuncUI.Elmish, 0.4.0"
open Elmish
open Avalonia
open Avalonia.Controls
open Avalonia.Controls.ApplicationLifetimes
open Avalonia.Input
open Avalonia.Layout
open Avalonia.FuncUI
open Avalonia.FuncUI.Components.Hosts
open Avalonia.FuncUI.DSL
open Avalonia.FuncUI.Elmish
module Counter =
type State = { count : int }
let init = { count = 0 }
type Msg = Increment | Decrement | Reset
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 [
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Reset)
Button.content "reset"
]
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Decrement)
Button.content "-"
]
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Increment)
Button.content "+"
]
TextBlock.create [
TextBlock.dock Dock.Top
TextBlock.fontSize 48.0
TextBlock.verticalAlignment VerticalAlignment.Center
TextBlock.horizontalAlignment HorizontalAlignment.Center
TextBlock.text (string state.count)
]
]
]
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
#if DEBUG
this.AttachDevTools(KeyGesture(Key.F12))
#endif
Elmish.Program.mkSimple (fun () -> Counter.init) Counter.update Counter.view
|> Program.withHost this
#if DEBUG
|> Program.withConsoleTrace
#endif
|> 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"
override this.OnFrameworkInitializationCompleted() =
match this.ApplicationLifetime with
| :? IClassicDesktopStyleApplicationLifetime as desktopLifetime ->
desktopLifetime.MainWindow <- MainWindow()
| _ -> ()
let main(args: string[]) =
AppBuilder
.Configure<App>()
.UsePlatformDetect()
.UseSkia()
.StartWithClassicDesktopLifetime(args)
main [||]
My suggestion would be to ask this question in the Avalonia repository itself, you can take the F# script as a sample |
Quick search gives https://github.com/moloneymb/AvaloniaLinuxFSIExample This is for plain Avalonia but I suppose it shouldn't be too complicated to adopt it for FuncUi If it won't be enough to make it work I'll try to take a look closer to this topic this evening or tomorrow. |
Thx a lot for the quick answer. Not sure how that slipped by. Will report back when i got it to work. |
This seems to work. Any suggestions for improvements? #r "nuget: JaggerJo.Avalonia.FuncUI"
open Avalonia
open Avalonia.Controls.ApplicationLifetimes
open Avalonia.Threading
open Avalonia.FuncUI
open FSharp.Compiler.Interactive
type App() =
inherit Application()
override this.Initialize() =
this.Styles.Load "resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"
this.Styles.Load "resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default"
override x.OnFrameworkInitializationCompleted() =
match x.ApplicationLifetime with
| :? IClassicDesktopStyleApplicationLifetime as desktopLifetime ->
desktopLifetime.ShutdownMode <- Controls.ShutdownMode.OnExplicitShutdown
| _ -> ()
let createApp(args) =
AppBuilder
.Configure<App>()
.UsePlatformDetect()
.UseSkia()
.StartWithClassicDesktopLifetime(args) |> ignore
let disp (f:unit -> 'a) =
Dispatcher.UIThread.InvokeAsync(f)
|> Async.AwaitTask
|> Async.RunSynchronously
fsi.EventLoop <- {new IEventLoop with
member x.Run() =
createApp ([|""|])
false //dummy
member x.Invoke(f) = disp f
member x.ScheduleRestart() = () //dummy
}
//Test
let w1 = Controls.Window()
w1.Width <- 600.
w1.Height <- 800.
w1.Background <- Media.Brushes.Aquamarine
let button1 = Controls.Button()
button1.Width <- 400.
button1.Height <- 100.
button1.Background <- Media.Brushes.Red
button1.Content <- "huhu"
button1.Click |> Observable.subscribe (fun _ -> button1.Content <- "hahahahahah")
w1.Content <- button1
w1.Show()
w1.Hide() |
Hmm, no longer works in a single fsx file and complains about application already running but Application.Current = null. Putting module AvEventLoop
open Avalonia
open Avalonia.Controls.ApplicationLifetimes
open Avalonia.Threading
open Avalonia.FuncUI
type App() =
inherit Application()
override this.Initialize() =
this.Styles.Load "resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"
this.Styles.Load "resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default"
override x.OnFrameworkInitializationCompleted() =
match x.ApplicationLifetime with
| :? IClassicDesktopStyleApplicationLifetime as desktopLifetime ->
desktopLifetime.ShutdownMode <- Controls.ShutdownMode.OnExplicitShutdown
| _ -> ()
let createApp(args) =
AppBuilder
.Configure<App>()
.UsePlatformDetect()
.UseSkia()
.StartWithClassicDesktopLifetime(args) |> ignore
let disp (f:unit -> 'a) =
Dispatcher.UIThread.InvokeAsync(f)
|> Async.AwaitTask
|> Async.RunSynchronously in a classlib, packing into a nuget and referencing in the fsx file: #i @"nuget: C:\Users\ehotw\source\repos\EHotwagner\AVEventLoop\AVLoop\bin\Debug"
#r "nuget: AVLoop"
open Avalonia
open FSharp.Compiler.Interactive
open AvEventLoop
fsi.EventLoop <- {new IEventLoop with
member x.Run() =
createApp ([|""|])
false //dummy
member x.Invoke(f) = disp f
member x.ScheduleRestart() = () //dummy
}
//Test. Wait first for the event loop to spin up.
let w1 = Controls.Window()
w1.Width <- 600.
w1.Height <- 800.
w1.Background <- Media.Brushes.Aquamarine
let button1 = Controls.Button()
button1.Width <- 400.
button1.Height <- 100.
button1.Background <- Media.Brushes.Red
button1.Content <- "huhu"
button1.Click |> Observable.subscribe (fun _ -> button1.Content <- "hahahahahah")
w1.Content <- button1
w1.Show()
// w1.Hide() seems ok. |
Maybe need to make sure that the application shuts down correctly with fsi. |
After installing the Eventloop and waiting for it to start up funcui is ready to go. #load @"C:\Users\ehotw\source\repos\EHotwagner\AVEventLoop\AVLoop\EventLoop.fsx"
open Elmish
open Avalonia.FuncUI.Elmish
open Avalonia
open Avalonia.Controls
open Avalonia.Layout
open Avalonia.FuncUI
open Avalonia.FuncUI.Components.Hosts
open Avalonia.FuncUI.DSL
module Counter =
type State = { count : int }
let init = { count = 0 }
type Msg = Increment | Decrement | Reset
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 [
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Reset)
Button.content "reset"
]
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Decrement)
Button.content "-"
]
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Increment)
Button.content "+"
]
TextBlock.create [
TextBlock.dock Dock.Top
TextBlock.fontSize 48.0
TextBlock.verticalAlignment VerticalAlignment.Center
TextBlock.horizontalAlignment HorizontalAlignment.Center
TextBlock.text (string state.count)
]
]
]
type MainWindow() as this =
inherit HostWindow()
do
base.Title <- "BasicTemplate"
base.Width <- 400.0
base.Height <- 400.0
Elmish.Program.mkSimple (fun () -> Counter.init) Counter.update Counter.view
|> Program.withHost this
|> Program.run
let win = MainWindow()
win.Show() Relevant points:
|
@EHotwagner thanks for the initial work on this. I have put the EventLoop in a nuget package AVLoop with a few enhancements. The repo includes samples for DataGrid and FuncUI. Unfortunately, still need to install EventLoop in a separate submit to FSI. It seems FSI main loop thread needs to be free to actually start the event loop. |
Happy new year,
I am currently looking at various gui frameworks to find one to use interactively with fsi. Preferable OS agnostic but windows might be sufficient. So naturally i looked at winforms first since it's supported out of the box. Works with VS2019 but not with VSCode because of dotnet/fsharp#9417 and Intellisense is still somewhat brittle.
So my question is, what would it take to get Avalonia and maybe funcui to work with fsi? Not sure how it could fit with the elmish way. I think one thing needed would be custom os specific event loops like https://stackoverflow.com/questions/5723823/fsi-wpf-event-loop since avalonia uses win32 windows on Win. I am just reading up on the Avalonia application lifecycle and any pointer/ideas would be helpful. Thx.
The text was updated successfully, but these errors were encountered: