-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathTry1.fs
153 lines (140 loc) · 6.48 KB
/
Try1.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
module CounterApp.Try1
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Media
open System.Reactive.Concurrency
open System.Reactive.Linq
open System.Reactive.Disposables
open FSharp.Control.Reactive
/// Subscribers
let do' f c = f c; Disposable.Empty
let prop s v c = Observable.subscribe (s c) v
let event s f c = (s c : IEvent<_,_>).Subscribe(fun v -> f c v)
let children clear add set (v1 : IObservable<IObservable<IObservable<_>>>) c = // Note: The previous versions of this have bugs.
let v2_disp = new SerialDisposable()
new CompositeDisposable(
v1.Subscribe(fun v2 ->
clear c
v2_disp.Disposable <-
let v3_disp = new CompositeDisposable()
let mutable i = 0
new CompositeDisposable(
v2.Subscribe (fun v3 ->
let i' = i
v3_disp.Add <| v3.Subscribe (fun v -> if i' < i then set c i' v else i <- add c v + 1)
),
v3_disp
)
),
v2_disp
)
:> IDisposable
let ui_element_collection v1 c = children (fun (c : UIElementCollection) -> c.Clear()) (fun c -> c.Add) (fun c i v -> c.RemoveAt i; c.Insert(i,v)) v1 c
/// Transformers
let control'<'a when 'a :> UIElement> (c : unit -> 'a) l =
Observable.Create (fun (sub : IObserver<_>) ->
let c = c()
let d = new CompositeDisposable()
List.iter (fun x -> d.Add(x c)) l
sub.OnNext(c)
d :> IDisposable
)
let control c l = control' c l :?> IObservable<UIElement>
let stack_panel' props childs = control StackPanel (List.append props [fun c -> ui_element_collection childs c.Children])
let stack_panel props childs = stack_panel' props (Observable.ofSeq childs |> Observable.single)
let window props content = control' Window (List.append props [prop (fun t v -> t.Content <- v) content])
/// The example
type Model = {
Count : int
Step : int
TimerOn : bool
}
type Msg =
| Increment
| Decrement
| Reset
| SetStep of int
| TimerToggled of bool
| TimedTick
let init = { Count = 0; Step = 1; TimerOn=false }
let pump = Subject.broadcast
let dispatch msg = CurrentThreadScheduler.Instance.Schedule(fun () -> pump.OnNext msg) |> ignore
let update =
pump
|> Observable.scanInit init (fun model msg ->
match msg with
| Increment -> { model with Count = model.Count + model.Step }
| Decrement -> { model with Count = model.Count - model.Step }
| Reset -> init
| SetStep n -> { model with Step = n }
| TimerToggled on -> { model with TimerOn = on }
| TimedTick -> if model.TimerOn then { model with Count = model.Count + model.Step } else model
)
|> Observable.publishInitial init
let cmd_timer =
update
|> Observable.distinctUntilChangedKey (fun x -> x.TimerOn)
|> Observable.map (fun model ->
if model.TimerOn then Observable.interval(TimeSpan.FromSeconds(1.0)) |> Observable.map (fun _ -> TimedTick)
else Observable.empty
)
|> Observable.switch
let cmd() =
Observable.mergeSeq [cmd_timer] // If there was more than one command handler I'd merge them here.
|> Observable.subscribe (fun x ->
Application.Current.Dispatcher.Invoke(fun () -> dispatch x)
)
let view =
window [ do' (fun t -> t.Title <- "Counter App")]
<| control Border [
do' (fun b -> b.Padding <- Thickness 30.0; b.BorderBrush <- Brushes.Black; b.Background <- Brushes.AliceBlue)
prop (fun b v -> b.Child <- v) <|
stack_panel [ do' (fun p -> p.VerticalAlignment <- VerticalAlignment.Center)] [
control Label [
do' (fun l -> l.HorizontalAlignment <- HorizontalAlignment.Center; l.HorizontalContentAlignment <- HorizontalAlignment.Center; l.Width <- 50.0)
prop (fun l v -> l.Content <- v) (update |> Observable.map (fun model -> sprintf "%d" model.Count))
]
control Button [
do' (fun b -> b.Content <- "Increment"; b.HorizontalAlignment <- HorizontalAlignment.Center)
event (fun b -> b.Click) (fun b arg -> dispatch Increment)
]
control Button [
do' (fun b -> b.Content <- "Decrement"; b.HorizontalAlignment <- HorizontalAlignment.Center)
event (fun b -> b.Click) (fun b arg -> dispatch Decrement)
]
control Border [
do' (fun b -> b.Padding <- Thickness 20.0)
prop (fun b v -> b.Child <- v) <|
stack_panel [do' (fun p -> p.Orientation <- Orientation.Horizontal; p.HorizontalAlignment <- HorizontalAlignment.Center)] [
control Label [do' (fun l -> l.Content <- "Timer")]
control CheckBox [
prop (fun c v -> c.IsChecked <- Nullable(v)) (update |> Observable.map (fun model -> model.TimerOn))
event (fun c -> c.Checked) (fun c v -> dispatch (TimerToggled true))
event (fun c -> c.Unchecked) (fun c v -> dispatch (TimerToggled false))
]
]
]
control Slider [
do' (fun s -> s.Minimum <- 0.0; s.Maximum <- 10.0; s.IsSnapToTickEnabled <- true)
prop (fun s v -> s.Value <- v) (update |> Observable.map (fun model -> model.Step |> float))
event (fun s -> s.ValueChanged) (fun c v -> dispatch (SetStep (int v.NewValue)))
]
control Label [
do' (fun l -> l.HorizontalAlignment <- HorizontalAlignment.Center)
prop (fun l v -> l.Content <- v) (update |> Observable.map (fun model -> sprintf "Step size: %d" model.Step))
]
control Button [
do' (fun b -> b.HorizontalAlignment <- HorizontalAlignment.Center; b.Content <- "Reset")
prop (fun b v -> b.IsEnabled <- v) (update |> Observable.map (fun model -> model <> init))
event (fun b -> b.Click) (fun b v -> dispatch Reset)
]
]
]
[<STAThread>]
let main _ =
let a = Application()
use __ = view.Subscribe (fun w -> a.MainWindow <- w; w.Show())
use __ = cmd()
use __ = update.Connect()
a.Run()