diff --git a/IfSharp.sln b/IfSharp.sln
index 26d8f5a..5fb5bdb 100644
--- a/IfSharp.sln
+++ b/IfSharp.sln
@@ -9,6 +9,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{3C993D34
ProjectSection(SolutionItems) = preProject
build.cmd = build.cmd
build.fsx = build.fsx
+ paket.dependencies = paket.dependencies
+ paket.lock = paket.lock
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ipython-profile", "ipython-profile", "{F5A3E866-86FB-44AD-9ED1-DB65D6EB0058}"
@@ -40,6 +42,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "IfSharp", "src\IfSharp\IfSh
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "IfSharp.Widgets", "src\IfSharp.Widgets\IfSharp.Widgets.fsproj", "{264A3F75-98C9-4B30-BA57-C54C16087C87}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "IfSharp.Kernel.Tests", "tests\IfSharp.Kernel.Tests\IfSharp.Kernel.Tests.fsproj", "{52627028-50B5-427F-BDDB-9DEADB72E7D6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -72,6 +76,14 @@ Global
{264A3F75-98C9-4B30-BA57-C54C16087C87}.Release|Any CPU.Build.0 = Release|Any CPU
{264A3F75-98C9-4B30-BA57-C54C16087C87}.Release|x64.ActiveCfg = Release|x64
{264A3F75-98C9-4B30-BA57-C54C16087C87}.Release|x64.Build.0 = Release|x64
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Debug|x64.ActiveCfg = Debug|x64
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Debug|x64.Build.0 = Debug|x64
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Release|x64.ActiveCfg = Release|x64
+ {52627028-50B5-427F-BDDB-9DEADB72E7D6}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/paket.dependencies b/paket.dependencies
index 6c7123c..dec78d6 100644
--- a/paket.dependencies
+++ b/paket.dependencies
@@ -13,8 +13,10 @@ nuget Newtonsoft.Json ~> 10.0.3
nuget FAKE >= 4.58.6
nuget xunit 2.1
nuget xunit.runner.console 2.1
+nuget xunit.runner.visualstudio
nuget Paket.Core ~> 5.194.0
nuget Trinet.Core.IO.Ntfs
+nuget PropertyChanged.Fody
#https://github.com/dotnet/corefx/issues/19914
# nuget System.Net.Http 4.3.1
\ No newline at end of file
diff --git a/paket.lock b/paket.lock
index 78b2d3d..da0ad90 100644
--- a/paket.lock
+++ b/paket.lock
@@ -7,6 +7,7 @@ NUGET
FSharp.Core (>= 4.0.1.7-alpha)
NETStandard.Library (>= 1.6)
FAKE (5.8.4)
+ Fody (3.3.5)
FSharp.Compiler.Service (25.0.1)
FSharp.Core (>= 4.1.18)
System.Collections.Immutable (>= 1.5)
@@ -27,6 +28,8 @@ NUGET
FSharp.Core (> 4.3)
Mono.Cecil (>= 0.10.0-beta6)
Newtonsoft.Json
+ PropertyChanged.Fody (2.6)
+ Fody (>= 3.3.2)
System.Collections.Immutable (1.5)
System.Reflection.Metadata (1.6)
System.Collections.Immutable (>= 1.5)
@@ -45,3 +48,4 @@ NUGET
xunit.extensibility.execution (2.1)
xunit.extensibility.core (2.1)
xunit.runner.console (2.1)
+ xunit.runner.visualstudio (2.4.1)
diff --git a/src/IfSharp.Kernel/Kernel.fs b/src/IfSharp.Kernel/Kernel.fs
index 621e951..755a9df 100644
--- a/src/IfSharp.Kernel/Kernel.fs
+++ b/src/IfSharp.Kernel/Kernel.fs
@@ -759,4 +759,7 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) =
//Async.Start (async { doHeartbeat() } )
Async.Start (async { doShell() } )
- Async.Start (async { doControl() } )
\ No newline at end of file
+ Async.Start (async { doControl() } )
+
+ /// Sends an update
+ member __.SendWidgetUpdate w = sendWidget w
\ No newline at end of file
diff --git a/src/IfSharp.Kernel/Printers.fs b/src/IfSharp.Kernel/Printers.fs
index 511443b..749ab69 100644
--- a/src/IfSharp.Kernel/Printers.fs
+++ b/src/IfSharp.Kernel/Printers.fs
@@ -14,6 +14,9 @@ type IWidget =
type IWidgetCollection =
abstract member GetChildren : unit -> IWidget[]
+type IKernel =
+ abstract member SendWidgetUpdate : IWidget -> unit
+
type WidgetDataDTO =
{
buffer_paths: string[]
diff --git a/src/IfSharp.Widgets/Color.fs b/src/IfSharp.Widgets/Color.fs
index 224d30c..7b22ac6 100644
--- a/src/IfSharp.Widgets/Color.fs
+++ b/src/IfSharp.Widgets/Color.fs
@@ -1,8 +1,5 @@
namespace IfSharp.Widgets
-open IfSharp.Kernel
-open Newtonsoft.Json
-
type ColorPicker() =
inherit DOMWidget(modelName = "ColorPickerModel", viewName = "ColorPickerView")
member val value = "" with get,set // Color('black', help="The color value.").tag(sync=True)
diff --git a/src/IfSharp.Widgets/FodyWeavers.xml b/src/IfSharp.Widgets/FodyWeavers.xml
new file mode 100644
index 0000000..4e68ed1
--- /dev/null
+++ b/src/IfSharp.Widgets/FodyWeavers.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/IfSharp.Widgets/FodyWeavers.xsd b/src/IfSharp.Widgets/FodyWeavers.xsd
new file mode 100644
index 0000000..2f1b8aa
--- /dev/null
+++ b/src/IfSharp.Widgets/FodyWeavers.xsd
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+ Used to control if the On_PropertyName_Changed feature is enabled.
+
+
+
+
+ Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.
+
+
+
+
+ Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.
+
+
+
+
+ Used to control if equality checks should use the Equals method resolved from the base class.
+
+
+
+
+ Used to control if equality checks should use the static Equals method resolved from the base class.
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/IfSharp.Widgets/IfSharp.Widgets.fsproj b/src/IfSharp.Widgets/IfSharp.Widgets.fsproj
index e508fa8..3e4d6b2 100644
--- a/src/IfSharp.Widgets/IfSharp.Widgets.fsproj
+++ b/src/IfSharp.Widgets/IfSharp.Widgets.fsproj
@@ -1,5 +1,6 @@
+
Debug
@@ -82,6 +83,7 @@
+
@@ -122,4 +124,16 @@
+
+
+
+
+ ..\..\packages\PropertyChanged.Fody\lib\net452\PropertyChanged.dll
+ True
+ True
+
+
+
+
+
\ No newline at end of file
diff --git a/src/IfSharp.Widgets/Widgets.fs b/src/IfSharp.Widgets/Widgets.fs
index 1959a4c..fa57cfd 100644
--- a/src/IfSharp.Widgets/Widgets.fs
+++ b/src/IfSharp.Widgets/Widgets.fs
@@ -39,6 +39,7 @@ module Internals =
)
open Internals
+open System.ComponentModel
/// A serializer that only supports writing instances of IWidget such that the notebook
/// can link the values together in the UI
@@ -115,6 +116,7 @@ type ButtonStyleSerializer() =
type Widget(modelName: string, viewName: string, ?modelModule, ?modelModuleVersion, ?viewModule, ?viewModuleVersion) as this =
let domClasses = ResizeArray<_>()
+ let ev = new Event<_,_>()
let key = WidgetManager.Register(this)
member val comm_id = key
@@ -134,6 +136,12 @@ type Widget(modelName: string, viewName: string, ?modelModule, ?modelModuleVersi
member __.RemoveClass(className) = domClasses.Remove className
+ member this.SendUpdate() =
+ App.Kernel |> Option.iter (fun k -> k.SendWidgetUpdate this)
+
+ []
+ member __.PropertyChanged = ev.Publish
+
interface IWidget with
member __.Key = key
@@ -145,6 +153,11 @@ type Widget(modelName: string, viewName: string, ?modelModule, ?modelModuleVersi
|> Seq.cast
|> Seq.toArray
+ interface INotifyPropertyChanged with
+
+ []
+ member __.PropertyChanged = ev.Publish
+
/// The WidgetManager contains an in-memory dictionary of all instances of Widget that
/// have been creates in order to keep track of UI element in the notebook
and WidgetManager() =
@@ -257,13 +270,20 @@ type DOMWidget(modelName, viewName, ?modelModule, ?modelModuleVersion, ?viewModu
[)>]
member val style = DescriptionStyle() with get, set
+type ValueWidget<'t>(modelName, viewName) =
+ inherit DOMWidget(modelName, viewName)
+ member val value : 't = Unchecked.defaultof<'t> with get,set
+
+ member this.OnvalueChanged() =
+ stdout.WriteLine("OnvalueChanged")
+ this.SendUpdate()
+
type Html(?value) =
inherit DOMWidget(modelName = "HTMLModel", viewName = "HTMLView")
member val value = defaultArg value "" with get,set
type IntSlider() =
- inherit DOMWidget(modelName = "IntSliderModel", viewName = "IntSliderView")
- member val value = 7 with get,set
+ inherit ValueWidget(modelName = "IntSliderModel", viewName = "IntSliderView")
member val min = 0 with get,set
member val max = 10 with get,set
member val step = 1 with get,set
@@ -283,8 +303,7 @@ type IntSlider() =
// indent : {True,False}
// indent the control to align with other controls with a description. The style.description_width attribute controls this width for consistence with other controls.
type Checkbox() =
- inherit DOMWidget(modelName = "CheckboxModel", viewName = "CheckboxView")
- member val value = false with get,set // Bool(False, help="Bool value").tag(sync=True)
+ inherit ValueWidget(modelName = "CheckboxModel", viewName = "CheckboxView")
member val disabled = false with get,set // Bool(False, help="Enable or disable user changes.").tag(sync=True)
member val indent = false with get,set // Bool(True, help="Indent the control to align with other controls with a description.").tag(sync=True)
@@ -300,7 +319,7 @@ type Checkbox() =
/// icon: str
/// font-awesome icon name
type ToggleButton() =
- inherit DOMWidget(modelName = "ToggleButtonModel", viewName = "ToggleButtonView")
+ inherit ValueWidget(modelName = "ToggleButtonModel", viewName = "ToggleButtonView")
member val value = false with get,set
member val tooltip = "" with get,set
@@ -315,7 +334,6 @@ type ToggleButton() =
// value: {True,False}
// value of the Valid widget
type Valid() =
- inherit DOMWidget(modelName = "ValidModel", viewName = "ValidView")
- member val value = false with get,set // Bool(False, help="Bool value").tag(sync=True)
+ inherit ValueWidget(modelName = "ValidModel", viewName = "ValidView")
member val disabled = false with get,set // Bool(False, help="Enable or disable user changes.").tag(sync=True)
member val readout = "" with get,set // Unicode('Invalid', help="Message displayed when the value is False").tag(sync=True)
diff --git a/src/IfSharp.Widgets/paket.references b/src/IfSharp.Widgets/paket.references
index fa39795..2caafa7 100644
--- a/src/IfSharp.Widgets/paket.references
+++ b/src/IfSharp.Widgets/paket.references
@@ -1,2 +1,3 @@
FSharp.Core
-Newtonsoft.JSON
\ No newline at end of file
+Newtonsoft.JSON
+PropertyChanged.Fody
\ No newline at end of file
diff --git a/src/IfSharp/App.config b/src/IfSharp/App.config
index 3392f17..a86e60b 100644
--- a/src/IfSharp/App.config
+++ b/src/IfSharp/App.config
@@ -6,7 +6,6 @@
-
True
@@ -67,6 +66,11 @@
+
+ True
+
+
+
True
diff --git a/tests/IfSharp.Kernel.Tests/ExampleTest.fs b/tests/IfSharp.Kernel.Tests/ExampleTest.fs
deleted file mode 100644
index faa8eb6..0000000
--- a/tests/IfSharp.Kernel.Tests/ExampleTest.fs
+++ /dev/null
@@ -1,11 +0,0 @@
-module ExampleTest
-
-open Xunit
-
-open IfSharp.Kernel
-
-[]
-let ``Trivial example test``() =
-
- let asSvg = Util.Svg "test"
- Assert.Equal("test", asSvg.Svg)
\ No newline at end of file
diff --git a/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj b/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj
index 07194f3..56f7833 100644
--- a/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj
+++ b/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj
@@ -9,7 +9,7 @@
Library
IfSharp.Kernel.Tests
IfSharp.Kernel.Tests
- v4.7.1
+ v4.7.2
4.3.1.0
IfSharp.Kernel.Tests
..\..\
@@ -73,6 +73,14 @@
+
+
+
+ <__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio
+
+
+
+
@@ -87,26 +95,28 @@
-->
+
-
-
+
-
-
IfSharp.Kernel
{2fe619b3-4756-4285-b31f-232607f62d78}
True
+
+ IfSharp.Widgets
+ {264a3f75-98c9-4b30-ba57-c54c16087c87}
+ True
+
-
@@ -174,6 +184,17 @@
+
+
+
+
+ ..\..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll
+ True
+ True
+
+
+
+
diff --git a/tests/IfSharp.Kernel.Tests/Scratch.fsx b/tests/IfSharp.Kernel.Tests/Scratch.fsx
deleted file mode 100644
index 5f28270..0000000
--- a/tests/IfSharp.Kernel.Tests/Scratch.fsx
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/tests/IfSharp.Kernel.Tests/WidgetTests.fs b/tests/IfSharp.Kernel.Tests/WidgetTests.fs
new file mode 100644
index 0000000..49f2421
--- /dev/null
+++ b/tests/IfSharp.Kernel.Tests/WidgetTests.fs
@@ -0,0 +1,80 @@
+module WidgetTests
+
+open Xunit
+open IfSharp.Widgets
+open IfSharp.Kernel
+open Newtonsoft.Json
+open System.IO
+
+let getParents (w: #IWidget) = w.GetParents()
+let getKey (w: #IWidget) = w.Key
+
+[]
+let propertyChangeEventsShouldWork() =
+ let propertiesChanged = ResizeArray()
+
+ let w = Html()
+ w.PropertyChanged.Add(fun x -> propertiesChanged.Add x.PropertyName)
+ w.description <- "description"
+
+ Assert.Equal(1, propertiesChanged.Count)
+ Assert.Equal("description", propertiesChanged |> Seq.head)
+
+[]
+let selectionContainerFormalConstructorShouldWork() =
+ let children =
+ [|
+ "Html1", Html() :> IWidget
+ "Html2", Html() :> IWidget
+ |]
+
+ let container = SelectionContainer("modelName", "viewName", children)
+ Assert.Equal(2, container.children.Length)
+ Assert.Equal(2, container._titles.Count)
+ Assert.Equal<_[]>([| children.[0] |> snd; children.[1] |> snd |], container.children)
+ Assert.Equal<_[]>([| children.[0] |> fst; children.[1] |> fst |], container._titles.Values |> Seq.toArray)
+
+[]
+let getParentsShouldWork() =
+ let dw = DOMWidget("modelName", "viewName")
+ let parents = getParents dw
+ Assert.Equal(2, parents.Length)
+ Assert.Equal(dw.layout, parents.[0] :?> Layout)
+ Assert.Equal(dw.style, parents.[1] :?> DescriptionStyle)
+
+[]
+let widgetSerializerShoulWriteWidget() =
+ use sw = new StringWriter()
+ use jw = new JsonTextWriter(sw)
+ let serializer = JsonSerializer()
+ let ws = WidgetSerializer()
+ let widget = Html()
+ ws.WriteJson(jw, widget, serializer)
+
+ let actual = sw.ToString()
+ Assert.Contains("IPY_MODEL", actual)
+ Assert.Contains(widget |> getKey |> string, actual)
+
+[]
+let widgetSerializerShouldWriteWidgetArray() =
+ use sw = new StringWriter()
+ use jw = new JsonTextWriter(sw)
+ let serializer = JsonSerializer()
+ let ws = WidgetSerializer()
+ let widgets =
+ [|
+ Html() :> IWidget
+ VBox() :> IWidget
+ HBox() :> IWidget
+ |]
+
+ ws.WriteJson(jw, widgets, serializer)
+
+ let actual = JsonConvert.DeserializeObject(sw.ToString())
+ Assert.Equal(3, actual.Length)
+ Assert.Contains("IPY_MODEL", actual.[0])
+ Assert.Contains("IPY_MODEL", actual.[1])
+ Assert.Contains("IPY_MODEL", actual.[2])
+ Assert.Contains(widgets.[0] |> getKey |> string, actual.[0])
+ Assert.Contains(widgets.[1] |> getKey |> string, actual.[1])
+ Assert.Contains(widgets.[2] |> getKey |> string, actual.[2])
diff --git a/tests/IfSharp.Kernel.Tests/app.config b/tests/IfSharp.Kernel.Tests/app.config
index 1a5fdea..8f38bec 100644
--- a/tests/IfSharp.Kernel.Tests/app.config
+++ b/tests/IfSharp.Kernel.Tests/app.config
@@ -1,9 +1,11 @@
-
+
-
+
+
+
True
@@ -44,6 +46,11 @@
+
+ True
+
+
+
True
@@ -59,4 +66,25 @@
-
\ No newline at end of file
+
+ True
+
+
+
+
+ True
+
+
+
+
+ True
+
+
+
+
+ True
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/IfSharp.Kernel.Tests/paket.references b/tests/IfSharp.Kernel.Tests/paket.references
index 5772b3d..69e8243 100644
--- a/tests/IfSharp.Kernel.Tests/paket.references
+++ b/tests/IfSharp.Kernel.Tests/paket.references
@@ -1,4 +1,6 @@
FSharp.Compiler.Service
Chessie
NetMQ
-xUnit
\ No newline at end of file
+xunit
+xunit.runner.visualstudio
+Newtonsoft.JSON
\ No newline at end of file