This commit is contained in:
Jérôme Laban 2019-11-10 22:45:48 -05:00
Родитель 76d58d20df
Коммит 76bc0cbc7a
16 изменённых файлов: 1030 добавлений и 110 удалений

Просмотреть файл

@ -21,7 +21,7 @@ module Reader =
let eventHandlerType =
match edef.EventType with
| :? GenericInstanceType as git -> git.FullName |> Text.removeDotNetGenericNotation
| _ -> "System.EventHandler"
| _ -> edef.EventType.FullName
{ Name = edef.Name
EventArgsType = eventArgsType |> Option.map convertTypeName |> Option.defaultValue "unit"

Просмотреть файл

@ -58,15 +58,27 @@ module Resolver =
|> Seq.filter (fun fdef -> fdef.IsStatic && fdef.FieldType.FullName = propertyBaseType && fdef.Name.EndsWith("Property"))
|> Seq.filter (fun fdef -> ``type``.Properties |> Seq.exists (fun pdef -> pdef.Name = fdef.Name.Replace("Property", "")) |> not)
|> Seq.toArray
let rec implementsInterface (``type``: TypeDefinition) (interfaceTypeName: string) =
let ifaces =
``type``.Interfaces.ToArray()
|> Array.filter (fun i -> i.InterfaceType.GetElementType().FullName.Equals(interfaceTypeName))
match ifaces with
| [||] -> match ``type``.BaseType with
| null -> false
| _ -> implementsInterface (``type``.BaseType.Resolve()) interfaceTypeName
| _ -> true
/// Finds all not settable list properties for a given type
let getAllListPropertiesWithNoSetterForType (``type``: TypeDefinition) =
let fullName = ``type``.FullName
if not ``type``.HasProperties then
[||]
else
``type``.Properties
|> Seq.filter (fun pdef -> pdef.GetMethod <> null && pdef.GetMethod.IsPublic && pdef.SetMethod = null)
|> Seq.filter (fun pdef -> pdef.PropertyType.GetElementType().FullName = "System.Collections.Generic.IList`1")
|> Seq.filter (fun pdef -> implementsInterface (pdef.PropertyType.Resolve()) "System.Collections.Generic.IList`1")
|> Seq.toArray
/// Finds all settable properties for a given type

Просмотреть файл

@ -4,6 +4,8 @@ namespace CounterApp
open Fabulous
open Fabulous.Uno
open System.Diagnostics
open Windows.UI.Xaml
open Windows.UI.Xaml.Controls
module App =
type Model =
@ -45,38 +47,36 @@ module App =
| TimedTick -> if model.TimerOn then { model with Count = model.Count + model.Step }, [ TickTimer ] else model, []
let view (model: Model) dispatch =
View.ContentPage(
content=View.StackLayout(padding = Thickness 30.0, verticalOptions = LayoutOptions.Center,
View.StackPanel(padding = Thickness 30.0, verticalAlignment = VerticalAlignment.Center,
children=[
View.Label(automationId="CountLabel", text=sprintf "%d" model.Count, horizontalOptions=LayoutOptions.Center, width=200.0, horizontalTextAlignment=TextAlignment.Center)
View.Button(automationId="IncrementButton", text="Increment", command= (fun () -> dispatch Increment))
View.Button(automationId="DecrementButton", text="Decrement", command= (fun () -> dispatch Decrement))
View.StackLayout(padding = Thickness 20.0, orientation=StackOrientation.Horizontal, horizontalOptions=LayoutOptions.Center,
children = [ View.Label(text="Timer")
View.Switch(automationId="TimerSwitch", isToggled=model.TimerOn, toggled=(fun on -> dispatch (TimerToggled on.Value))) ])
View.Slider(automationId="StepSlider", minimumMaximum=(0.0, 10.0), value= double model.Step, valueChanged=(fun args -> dispatch (SetStep (int (args.NewValue + 0.5)))))
View.Label(automationId="StepSizeLabel", text=sprintf "Step size: %d" model.Step, horizontalOptions=LayoutOptions.Center)
View.Button(text="Reset", horizontalOptions=LayoutOptions.Center, command=(fun () -> dispatch Reset), commandCanExecute = (model <> initModel () ))
]))
View.TextBlock(text=sprintf "%d" model.Count, horizontalAlignment=HorizontalAlignment.Center, width=200.0, horizontalTextAlignment=TextAlignment.Center)
View.Button(content="Increment", command= (fun () -> dispatch Increment))
View.Button(content="Decrement", command= (fun () -> dispatch Decrement))
View.TextBlock(text=sprintf "Step size: %d" model.Step, horizontalAlignment=HorizontalAlignment.Center)
View.Button(content="Reset", horizontalAlignment=HorizontalAlignment.Center, command=(fun () -> dispatch Reset), commandCanExecute = (model <> initModel () ))
])
let program =
Program.mkProgramWithCmdMsg init update view mapCmdMsgToCmd
type CounterApp () as app =
inherit Application ()
let runner =
override u.OnLaunched activatedArgs =
Windows.UI.Xaml.GenericStyles.Initialize()
Windows.ApplicationModel.Resources.ResourceLoader.DefaultLanguage <- "en-US"
Windows.ApplicationModel.Resources.ResourceLoader.AddLookupAssembly(System.Reflection.Assembly.Load("Uno.UI, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null"))
App.program
|> Program.withConsoleTrace
|> XamarinFormsProgram.run app
|> UnoProgram.run app
#if DEBUG
// Run LiveUpdate using:
//
do runner.EnableLiveUpdate ()
// do runner.EnableLiveUpdate ()
#endif

Просмотреть файл

@ -1,13 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Exe</OutputType>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<WasmShellILLinkerEnabled>false</WasmShellILLinkerEnabled>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<MonoRuntimeDebuggerEnabled>true</MonoRuntimeDebuggerEnabled>
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="WasmCSS\*.css" />
<EmbeddedResource Include="WasmScripts\*.js" />
</ItemGroup>
<ItemGroup>
<None Include="WasmScripts\AppManifest.js" />
<Compile Include="CounterApp.fs" />
<Compile Include="main.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Fabulous.Uno\Fabulous.Uno.fsproj" />
<PackageReference Include="Uno.UI" />
<PackageReference Include="Uno.Wasm.Bootstrap" />
<DotNetCliToolReference Include="Uno.Wasm.Bootstrap.Cli" />
</ItemGroup>
<ItemGroup />
<Import Project="..\..\..\Packages.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:60376/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"CounterApp": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:60377/"
}
}
}

Просмотреть файл

@ -0,0 +1,7 @@
var UnoAppManifest = {
splashScreenImage: "Assets/SplashScreen.scale-200.png",
splashScreenColor: "#00f",
displayName: "App13"
}

Просмотреть файл

@ -0,0 +1,8 @@
open System
open Windows.UI.Xaml
[<EntryPoint; STAThread>]
let main argv =
Application.Start(fun _ -> new CounterApp.CounterApp() |> ignore)
0

Просмотреть файл

@ -5,19 +5,22 @@ namespace Fabulous.Uno
open Fabulous
open Windows.UI.Xaml
open Windows.UI.Core
open Microsoft.Extensions.Logging
open Windows.UI.Xaml.Controls
type UnoHost(app: Application) =
interface IHost with
member __.GetRootView() =
match Window.Current.Content with
| null -> failwith "No root view"
| rootView -> rootView :> obj
| rootView -> (rootView :?> Frame).Content
member __.SetRootView(rootView) =
match rootView with
| :? FrameworkElement as element -> Window.Current.Content <- element
| :? FrameworkElement as element -> (Window.Current.Content :?> Frame).Content <- element
| _ -> failwithf "Incorrect model type: expected a FrameworkElement but got a %O" (rootView.GetType())
/// Program module - functions to manipulate program instances
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
@ -32,14 +35,34 @@ module UnoProgram =
Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, fun () -> fn()) |> ignore
()
let configureFilters (factory: ILoggerFactory) =
let settings = new FilterLoggerSettings()
settings.Add("Uno", LogLevel.Warning);
settings.Add("Windows", LogLevel.Warning);
factory
.WithFilter(settings)
.AddConsole(LogLevel.Debug)
let runWith app arg program =
let host = UnoHost(app)
typeof<Uno.UI.Wasm.WasmHttpHandler>.ToString() |> ignore
configureFilters Uno.Extensions.LogExtensionPoint.AmbientLoggerFactory |> ignore
let rootWindow = new Frame()
rootWindow.Background <- SolidColorBrushHelper.Black
Window.Current.Content <- rootWindow
Window.Current.Activate()
program
|> Program.withCanReuseView ViewHelpers.canReuseView
|> Program.withSyncDispatch syncDispatch
|> Program.withSyncAction syncAction
|> Program.runWithFabulous host arg
|> ignore
let run app program =
runWith app () program

Просмотреть файл

@ -8,6 +8,7 @@ open System.Collections.ObjectModel
open Windows.UI.Xaml.Controls
open Windows.UI.Xaml.Media
open Windows.UI.Xaml
open Windows.UI.Xaml.Controls.Primitives
module CollectionHelpers =
/// Try and find a specific ListView item
@ -94,7 +95,18 @@ module ViewConverters =
/////////////////
/// Event Handlers
/////////////////
let makeToggledEventHandler f =
System.EventHandler(fun sender args ->
let toggleSwitch = sender :?> ToggleSwitch
f (toggleSwitch.IsOn)
)
let makeValueChangedEventHandler f =
System.EventHandler(fun sender args ->
let rangeBase = sender :?> RangeBase
f (rangeBase.Value)
)
//let makeCurrentPageChanged<'a when 'a :> Xamarin.Forms.Page and 'a : null> f =
// System.EventHandler(fun sender _args ->
// let control = sender :?> Xamarin.Forms.MultiPage<'a>

Просмотреть файл

@ -338,33 +338,33 @@ module ViewUpdaters =
// match prevValueOpt with ValueNone -> () | ValueSome f -> target.SizeAllocated.RemoveHandler(f)
// match valueOpt with ValueNone -> () | ValueSome f -> target.SizeAllocated.AddHandler(f)
///// Converts an F# function to a Xamarin.Forms ICommand
//let makeCommand f =
// let ev = Event<_,_>()
// { new ICommand with
// member __.add_CanExecuteChanged h = ev.Publish.AddHandler h
// member __.remove_CanExecuteChanged h = ev.Publish.RemoveHandler h
// member __.CanExecute _ = true
// member __.Execute _ = f() }
/// Converts an F# function to a Xamarin.Forms ICommand
let makeCommand f =
let ev = Event<_,_>()
{ new ICommand with
member __.add_CanExecuteChanged h = ev.Publish.AddHandler h
member __.remove_CanExecuteChanged h = ev.Publish.RemoveHandler h
member __.CanExecute _ = true
member __.Execute _ = f() }
///// Converts an F# function to a Xamarin.Forms ICommand, with a CanExecute value
//let makeCommandCanExecute f canExecute =
// let ev = Event<_,_>()
// { new ICommand with
// member __.add_CanExecuteChanged h = ev.Publish.AddHandler h
// member __.remove_CanExecuteChanged h = ev.Publish.RemoveHandler h
// member __.CanExecute _ = canExecute
// member __.Execute _ = f() }
/// Converts an F# function to a Xamarin.Forms ICommand, with a CanExecute value
let makeCommandCanExecute f canExecute =
let ev = Event<_,_>()
{ new ICommand with
member __.add_CanExecuteChanged h = ev.Publish.AddHandler h
member __.remove_CanExecuteChanged h = ev.Publish.RemoveHandler h
member __.CanExecute _ = canExecute
member __.Execute _ = f() }
///// Update the Command and CanExecute properties of a control, given previous and current values
//let inline updateCommand prevCommandValueOpt commandValueOpt argTransform setter prevCanExecuteValueOpt canExecuteValueOpt target =
// match prevCommandValueOpt, prevCanExecuteValueOpt, commandValueOpt, canExecuteValueOpt with
// | ValueNone, ValueNone, ValueNone, ValueNone -> ()
// | ValueSome prevf, ValueNone, ValueSome f, ValueNone when identical prevf f -> ()
// | ValueSome prevf, ValueSome prevx, ValueSome f, ValueSome x when identical prevf f && prevx = x -> ()
// | _, _, ValueNone, _ -> setter target null
// | _, _, ValueSome f, ValueNone -> setter target (makeCommand (fun () -> f (argTransform target)))
// | _, _, ValueSome f, ValueSome k -> setter target (makeCommandCanExecute (fun () -> f (argTransform target)) k)
let inline updateCommand prevCommandValueOpt commandValueOpt argTransform setter prevCanExecuteValueOpt canExecuteValueOpt target =
match prevCommandValueOpt, prevCanExecuteValueOpt, commandValueOpt, canExecuteValueOpt with
| ValueNone, ValueNone, ValueNone, ValueNone -> ()
| ValueSome prevf, ValueNone, ValueSome f, ValueNone when identical prevf f -> ()
| ValueSome prevf, ValueSome prevx, ValueSome f, ValueSome x when identical prevf f && prevx = x -> ()
| _, _, ValueNone, _ -> setter target null
| _, _, ValueSome f, ValueNone -> setter target (makeCommand (fun () -> f (argTransform target)))
| _, _, ValueSome f, ValueSome k -> setter target (makeCommandCanExecute (fun () -> f (argTransform target)) k)
///// Update the CurrentPage of a control, given previous and current values
//let updateMultiPageOfTCurrentPage<'a when 'a :> Xamarin.Forms.Page> prevValueOpt valueOpt (target: Xamarin.Forms.MultiPage<'a>) =
@ -374,24 +374,24 @@ module ViewUpdaters =
// | ValueSome _, ValueNone -> target.CurrentPage <- Unchecked.defaultof<'a>
// | _, ValueSome curr -> target.CurrentPage <- target.Children.[curr]
///// Update the Minium and Maximum values of a slider, given previous and current values
//let updateSliderMinimumMaximum prevValueOpt valueOpt (target: obj) =
// let control = target :?> Xamarin.Forms.Slider
// let defaultValue = (0.0, 1.0)
// let updateFunc (_, prevMaximum) (newMinimum, newMaximum) =
// if newMinimum > prevMaximum then
// control.Maximum <- newMaximum
// control.Minimum <- newMinimum
// else
// control.Minimum <- newMinimum
// control.Maximum <- newMaximum
/// Update the Minium and Maximum values of a slider, given previous and current values
let updateRangeBaseMinimumMaximum prevValueOpt valueOpt (target: obj) =
let control = target :?> Slider
let defaultValue = (0.0, 1.0)
let updateFunc (_, prevMaximum) (newMinimum, newMaximum) =
if newMinimum > prevMaximum then
control.Maximum <- newMaximum
control.Minimum <- newMinimum
else
control.Minimum <- newMinimum
control.Maximum <- newMaximum
// match prevValueOpt, valueOpt with
// | ValueNone, ValueNone -> ()
// | ValueSome prev, ValueSome curr when prev = curr -> ()
// | ValueSome prev, ValueSome curr -> updateFunc prev curr
// | ValueSome prev, ValueNone -> updateFunc prev defaultValue
// | ValueNone, ValueSome curr -> updateFunc defaultValue curr
match prevValueOpt, valueOpt with
| ValueNone, ValueNone -> ()
| ValueSome prev, ValueSome curr when prev = curr -> ()
| ValueSome prev, ValueSome curr -> updateFunc prev curr
| ValueSome prev, ValueNone -> updateFunc prev defaultValue
| ValueNone, ValueSome curr -> updateFunc defaultValue curr
///// Update the Minimum and Maximum values of a stepper, given previous and current values
//let updateStepperMinimumMaximum prevValueOpt valueOpt (target: obj) =

Просмотреть файл

@ -11,12 +11,17 @@
<Compile Include="..\Fabulous.Uno.Core\CustomControls.fs" />
<Compile Include="..\Fabulous.Uno.Core\ViewHelpers.fs" />
<Compile Include="..\Fabulous.Uno.Core\ViewConverters.fs" />
<Compile Include="..\Fabulous.Uno.Core\Program.fs" />
<Compile Include="..\Fabulous.Uno.Core\ViewUpdaters.fs" />
<Compile Include="..\Fabulous.Uno.Core\ViewExtensions.fs" />
<Compile Include="Uno.UI.fs" />
<None Include="Uno.UI.json" />
</ItemGroup>
<ItemGroup>
<!-- Note that for WebAssembly version 1.1.1 of the console logger required -->
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.1" />
<PackageReference Include="Uno.UI" />
<ProjectReference Include="..\..\..\src\Fabulous\Fabulous.fsproj" />
</ItemGroup>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -36,6 +36,12 @@
},
{
"source": "Height"
},
{
"source": "HorizontalAlignment"
},
{
"source": "VerticalAlignment"
}
],
"events": [
@ -49,14 +55,110 @@
]
},
{
"type": "Windows.UI.Xaml.Controls.Panel",
"type": "Windows.UI.Xaml.Controls.ContentControl",
"canBeInstantiated": true,
"properties": [
{
"source": "Content"
}
],
"events": [
]
},
{
"type": "Windows.UI.Xaml.Controls.Primitives.ButtonBase",
"canBeInstantiated": false,
"properties": [
{
"source": "Command"
}
],
"events": [
]
},
{
"type": "Windows.UI.Xaml.Controls.Button",
"canBeInstantiated": true,
"properties": [
],
"events": [
]
},
{
"type": "Windows.UI.Xaml.Controls.Primitives.RangeBase",
"canBeInstantiated": false,
"properties": [
{
"source": "Value"
},
{
"source": null,
"name": "MinimumMaximum",
"defaultValue": "(0,100)",
"inputType": "double * double",
"updateCode": "ViewUpdaters.updateRangeBaseMinimumMaximum"
}
],
"events": [
{
"source": "ValueChanged",
"inputType": "double -> unit",
"convertInputToModel": "ViewConverters.makeValueChangedEventHandler",
"relatedProperties": [
"Value"
]
}
]
},
{
"type": "Windows.UI.Xaml.Controls.Slider",
"canBeInstantiated": true,
"properties": [
],
"events": [
],
"primaryConstructorMembers": [
"Children"
"Value"
]
},
{
"type": "Windows.UI.Xaml.Controls.ToggleSwitch",
"canBeInstantiated": true,
"properties": [
{
"source": "IsOn"
}
],
"events": [
{
"source": "Toggled",
"inputType": "bool -> unit",
"convertInputToModel": "ViewConverters.makeToggledEventHandler",
"relatedProperties": [
"IsOn"
]
}
],
"primaryConstructorMembers": [
"IsOn"
]
},
{
"type": "Windows.UI.Xaml.Controls.Panel",
"canBeInstantiated": false,
"properties": [
{
"source": "Padding"
},
{
"source": "Children",
"inputType": "ViewElement list",
"collection": {
"elementType": "Windows.UI.Xaml.UIElement"
}
}
],
"events": [
]
},
{
@ -68,6 +170,9 @@
}
],
"events": [
],
"primaryConstructorMembers": [
"Children"
]
},
{
@ -76,8 +181,12 @@
"properties": [
{
"source": "Orientation"
} ],
}
],
"events": [
],
"primaryConstructorMembers": [
"Children"
]
},
{
@ -87,6 +196,9 @@
{
"source": "Text"
},
{
"source": "HorizontalTextAlignment"
},
{
"source": "Foreground"
}

Просмотреть файл

@ -28,18 +28,14 @@ module Reflection =
match ``type``.ContainsGenericParameters with
| true -> return None // Generic types are not supported
| false ->
let dependencyPropertyFieldInfo = ``type``.GetField(propertyName + "Property")
let dependencyPropertyInfo = ``type``.GetProperty(propertyName + "Property")
if dependencyPropertyInfo <> null || dependencyPropertyFieldInfo <> null then
let filteredProperties = ``type``.GetProperties() |> Array.filter (fun m -> m.Name.Equals(propertyName))
return match filteredProperties with
| [|property|] -> Some {
Name = propertyName
Type = property.GetMethod.ReturnType |> toCleanTypeName
DefaultValue = null (*property.GetMetadata(``type``).DefaultValue*) }
| _ -> None
else
return None
let fullName = ``type``.FullName
let filteredProperties = ``type``.GetProperties() |> Array.filter (fun m -> m.Name.Equals(propertyName))
return match filteredProperties with
| [||] -> None
| _ -> Some {
Name = propertyName
Type = (filteredProperties |> Array.head).GetMethod.ReturnType |> toCleanTypeName
DefaultValue = null (*property.GetMetadata(``type``).DefaultValue*) }
}

Просмотреть файл

@ -26,6 +26,9 @@ module UnoConverters =
| "System.EventHandler<Xamarin.Forms.VisualElement/FocusRequestArgs>" -> "System.EventHandler<Xamarin.Forms.VisualElement.FocusRequestArgs>"
| "System.EventHandler<System.Tuple<System.String,System.String>>" -> "System.EventHandler<string * string>"
| "System.Tuple<System.String,System.String>" -> "(string * string)"
//| "Windows.UI.Xaml.RoutedEventHandler" -> "System.EventHandler<Windows.UI.Xaml.FrameworkElement * System.Object>"
//| "Windows.Foundation.TypedEventHandler<Windows.UI.Xaml.FrameworkElement,System.Object>" -> "System.EventHandler<Windows.UI.Xaml.FrameworkElement * System.Object>"
| _ -> Converters.convertTypeName typeName
let rec tryGetStringRepresentationOfDefaultValue (defaultValue: obj) : string option =

Просмотреть файл

@ -28,6 +28,8 @@
<PackageReference Update="Microsoft.FSharpLu.Json" Version="0.11.2" />
<PackageReference Update="CommandLineParser.FSharp" Version="2.5.0" />
<PackageReference Update="Uno.UI" Version="2.0.512-dev.3874" />
<PackageReference Update="Uno.UI.Bootstrap" Version="1.0.0" />
<DotNetCliToolReference Update="Uno.UI.Bootstrap.Cli" Version="1.0.0" />
</ItemGroup>
<PropertyGroup>
<RestoreSources>