-
Notifications
You must be signed in to change notification settings - Fork 1
/
CounterGrid.elm
145 lines (118 loc) · 3.63 KB
/
CounterGrid.elm
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
module CounterGrid exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Time
import Counter
import List
import Debug
import Random
import Window
import Task
import Array as A exposing (Array, get)
import Html.Lazy exposing (lazy)
-- MODEL
globals =
let
tdPx = 20
in
{ tdPx = tdPx
, tdSize = (toString tdPx) ++ "px"
, textSize = 16
, border = "0px solid gray"
, w = 40
, h = 24
}
type alias Model =
{ counters : Maybe (Array Counter.Model)
, cols : Maybe Int
, rows : Maybe Int
}
init : (Model, Cmd Msg)
init =
({ counters = Nothing
, cols = Nothing
, rows = Nothing
}, Task.perform Resize Window.size)
periodGen : Int -> Random.Generator (List Float)
periodGen n = Random.list n (Random.float 300 30000)
-- UPDATE
type Msg =
-- Pass the tick to each counter so they can update if their period is over.
Tick Time.Time
-- Not used in practice
| CounterMsg Counter.Msg
-- Used only once at start of app, when the random period lengths are generated.
| Rnds (List Float)
-- Used to determine how many cols/rows we need to fill the screen.
| Resize Window.Size
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Tick time ->
case model.counters of
Just cs ->
({ model
| counters = Just <| A.map (Counter.update <| Counter.tickMsg time) cs
}, Cmd.none)
-- Ignore ticks if the counters haven't been initialized yet.
Nothing -> (model, Cmd.none)
CounterMsg counterMsg ->
(model, Cmd.none)
-- Once we receive a list of periods, we can initialize the counters with them.
Rnds periods ->
({ model
| counters = Just <| A.map (\p -> Counter.init globals.textSize p) (A.fromList periods)
}, Cmd.none)
-- Once we have a window size we know how many counters we'll need, so we can generate period lengths.
Resize windowSize ->
let
cols = windowSize.width // globals.tdPx
rows = windowSize.height // globals.tdPx
m = { model
| cols = Just cols
, rows = Just rows
}
in
(m, Random.generate Rnds (periodGen <| cols*rows))
-- VIEW
viewTable : Model -> Html Msg
viewTable model =
case (model.counters, model.cols, model.rows) of
(Just cs, Just cols, Just rows) ->
let
n = A.length cs
w = cols
h = (n // cols) + 1
row r = tr [] <| A.toList <| A.map (lazy cellFor) <| A.slice (r*h) (r*h + w) cs
cellFor counter = td [tdStyle] [Html.map CounterMsg <| lazy Counter.view counter]
in
div []
[ table [tableStyle] (List.map row <| List.range 0 (h-1))
]
(_, _, _) -> div [] []
tdStyle = style
[ ("width", globals.tdSize)
, ("height", globals.tdSize)
]
tableStyle = style
[ ("background", "black")
, ("border", globals.border)
, ("margin", "0 auto")
]
view : Model -> Html Msg
view model =
div [ style
[ ("background", "black")
, ("position", "absolute")
, ("width", "100%")
, ("height", "100%")
]
]
[ viewTable model
]
-- APP
subscriptions : Model -> Sub Msg
subscriptions model = Sub.batch
[ Time.every Time.millisecond Tick
, Window.resizes Resize
]