Mercurial > gui
comparison calendar.ur @ 14:0827320b0f04
Write calendarCtl in terms of a source with a listener.
author | Karn Kallio <kkallio@eka> |
---|---|
date | Fri, 05 Aug 2011 18:55:24 -0430 |
parents | |
children | 8300d5f0dc19 |
comparison
equal
deleted
inserted
replaced
13:c016beb0ebac | 14:0827320b0f04 |
---|---|
1 style calendar | |
2 style prev | |
3 style this | |
4 style next | |
5 style weekday | |
6 style curday | |
7 style otherday | |
8 style selday | |
9 | |
10 datatype month = Prev | This | Next | |
11 | |
12 type date = {Year : int, Month : int, Day : int} | |
13 val date_eq = mkEq (fn {Year = y, Month = m, Day = d} {Year = y', Month = m', Day = d'} => | |
14 y = y' && m = m' && d = d') | |
15 val date_ord = mkOrd {Lt = fn {Year = y1, Month = m1, Day = d1} {Year = y2, Month = m2, Day = d2} => | |
16 y1 < y2 || (y1 = y2 && m1 < m2) || (y1 = y2 && m1 = m2 && d1 < d2), | |
17 Le = fn {Year = y1, Month = m1, Day = d1} {Year = y2, Month = m2, Day = d2} => | |
18 y1 < y2 || (y1 = y2 && m1 < m2) || (y1 = y2 && m1 = m2 && d1 <= d2)} | |
19 | |
20 type t = {Month : source {ThisMonth : int, Year : int, | |
21 ThisMonthLength : int, PrevMonthLength : int, | |
22 MondayMonth : month, MondayDay : int, | |
23 PrevMonthName : string, ThisMonthName : string, NextMonthName : string}, | |
24 Day : ChangePoller.changePoller date} | |
25 | |
26 fun pad len n = | |
27 let | |
28 val s = show n | |
29 | |
30 fun pad' len s = | |
31 if len <= 0 then | |
32 s | |
33 else | |
34 pad' (len-1) ("0" ^ s) | |
35 in | |
36 pad' (len - String.length s) s | |
37 end | |
38 | |
39 fun time {Year = y, Month = m, Day = d} = readError (show y ^ "-" ^ pad 2 m ^ "-" ^ pad 2 d ^ " 00:00:00") | |
40 fun date tm = | |
41 let | |
42 val y = readError (timef "%Y" tm) | |
43 val m = readError (timef "%m" tm) | |
44 val d = readError (timef "%d" tm) | |
45 in | |
46 {Year = y, Month = m, Day = d} | |
47 end | |
48 | |
49 fun monthLen m = | |
50 let | |
51 fun f n tm = | |
52 let | |
53 val next = addSeconds tm (60 * 60 * 24) | |
54 val nextMon = readError (timef "%m" next) | |
55 in | |
56 if nextMon = m.Month then | |
57 f (n+1) next | |
58 else | |
59 n | |
60 end | |
61 in | |
62 f 28 (readError (show m.Year ^ "-" ^ pad 2 m.Month ^ "-28 00:00:00")) | |
63 end | |
64 | |
65 fun weekdayToNum s = | |
66 case s of | |
67 "Mon" => 0 | |
68 | "Tue" => 1 | |
69 | "Wed" => 2 | |
70 | "Thu" => 3 | |
71 | "Fri" => 4 | |
72 | "Sat" => 5 | |
73 | "Sun" => 6 | |
74 | _ => error <xml>Datebox: Bad weekday name</xml> | |
75 | |
76 fun timeOfMonth my = | |
77 readError (show my.Year ^ "-" ^ pad 2 my.Month ^ "-01 00:00:00") | |
78 | |
79 fun monthInfo' this = | |
80 let | |
81 val prev = if this.Month = 1 then | |
82 {Month = 12, Year = this.Year - 1} | |
83 else | |
84 {Month = this.Month-1, Year = this.Year} | |
85 val prevLen = monthLen prev | |
86 | |
87 val next = if this.Month = 12 then | |
88 {Month = 1, Year = this.Year + 1} | |
89 else | |
90 {Month = this.Month + 1, Year = this.Year} | |
91 | |
92 val firstDow = weekdayToNum (timef "%a" (timeOfMonth this)) | |
93 in | |
94 {ThisMonth = this.Month, Year = this.Year, | |
95 ThisMonthLength = monthLen this, | |
96 PrevMonthLength = prevLen, | |
97 MondayMonth = if firstDow = 0 then This else Prev, | |
98 MondayDay = if firstDow = 0 then 1 else prevLen - (firstDow - 1), | |
99 PrevMonthName = timef "%b" (timeOfMonth prev), | |
100 ThisMonthName = timef "%b" (timeOfMonth this), | |
101 NextMonthName = timef "%b" (timeOfMonth next)} | |
102 end | |
103 | |
104 fun monthInfo this = return (monthInfo' this) | |
105 | |
106 fun create tm = | |
107 year <- return (readError (timef "%Y" tm)); | |
108 month <- return (readError (timef "%m" tm)); | |
109 minf <- source (monthInfo' {Month = month, Year = year}); | |
110 day <- return (readError (timef "%d" tm)); | |
111 day <- ChangePoller.create {Year = year, Month = month, Day = day}; | |
112 return {Month = minf, Day = day} | |
113 | |
114 fun render' t = | |
115 minf <- signal t.Month; | |
116 let | |
117 fun rows year month day = | |
118 let | |
119 fun row year month day weekday = | |
120 if weekday >= 7 then | |
121 <xml/> | |
122 else | |
123 <xml> | |
124 <dyn signal={let | |
125 val thisDate = {Year = year, | |
126 Month = case month of | |
127 Prev => if minf.ThisMonth = 1 then 12 else minf.ThisMonth - 1 | |
128 | This => minf.ThisMonth | |
129 | Next => if minf.ThisMonth = 12 then 1 else minf.ThisMonth + 1, | |
130 Day = day} | |
131 in | |
132 cday <- ChangePoller.value t.Day; | |
133 return (if Record.equal thisDate cday then | |
134 <xml><td class={selday}>{[day]}</td></xml> | |
135 else case month of | |
136 This => <xml><td class={curday} | |
137 onclick={set (ChangePoller.ctl t.Day) thisDate}>{[day]}</td></xml> | |
138 | _ => <xml><td class={otherday}>{[day]}</td></xml>) | |
139 end}/> | |
140 {let | |
141 val (year, month, day) = | |
142 case month of | |
143 Prev => if day = minf.PrevMonthLength then | |
144 (if minf.ThisMonth = 1 then year + 1 else year, This, 1) | |
145 else | |
146 (year, Prev, day+1) | |
147 | This => if day = minf.ThisMonthLength then | |
148 (if minf.ThisMonth = 12 then year + 1 else year, Next, 1) | |
149 else | |
150 (year, This, day+1) | |
151 | Next => (year, Next, day+1) | |
152 in | |
153 row year month day (weekday+1) | |
154 end} | |
155 </xml> | |
156 in | |
157 case month of | |
158 Next => <xml/> | |
159 | _ => | |
160 <xml> | |
161 <tr>{row year month day 0}</tr> | |
162 {let | |
163 val next = | |
164 case month of | |
165 Prev => Some (if minf.ThisMonth = 1 then year + 1 else year, This, day + 7 - minf.PrevMonthLength) | |
166 | This => | |
167 Some (if day + 7 > minf.ThisMonthLength then | |
168 (if minf.ThisMonth = 12 then year + 1 else year, Next, day + 7 - minf.ThisMonthLength) | |
169 else | |
170 (year, This, day + 7)) | |
171 | Next => None | |
172 in | |
173 case next of | |
174 None => <xml/> | |
175 | Some (year, month, day) => rows year month day | |
176 end} | |
177 </xml> | |
178 end | |
179 in | |
180 return <xml> | |
181 <table class={calendar}> | |
182 <tr> | |
183 <th class={prev} colspan={2} | |
184 onclick={minf <- rpc (monthInfo (if minf.ThisMonth = 1 then | |
185 {Month = 12, Year = minf.Year - 1} | |
186 else | |
187 {Month = minf.ThisMonth - 1, Year = minf.Year})); | |
188 set t.Month minf}><< {[minf.PrevMonthName]}</th> | |
189 <th class={this} colspan={3}>{[minf.ThisMonthName]} {[minf.Year]}</th> | |
190 <th class={next} colspan={2} | |
191 onclick={minf <- rpc (monthInfo (if minf.ThisMonth = 12 then | |
192 {Month = 1, Year = minf.Year + 1} | |
193 else | |
194 {Month = minf.ThisMonth + 1, Year = minf.Year})); | |
195 set t.Month minf}>{[minf.NextMonthName]} >></th> | |
196 </tr> | |
197 <tr class={weekday}> <th>M</th> <th>Tu</th> <th>W</th> <th>Th</th> <th>F</th> <th>Sa</th> <th>Su</th> </tr> | |
198 {rows (if minf.ThisMonth = 1 && (case minf.MondayMonth of This => False | _ => True) then | |
199 minf.Year - 1 | |
200 else | |
201 minf.Year) minf.MondayMonth minf.MondayDay} | |
202 </table> | |
203 </xml> | |
204 end | |
205 | |
206 fun addListener f ctl = | |
207 ChangePoller.addChangeListener f 100 ctl.Day | |
208 | |
209 val gui_t = Gui.mkGui | |
210 (fn ctl => | |
211 <xml> | |
212 <dyn signal={render' ctl}/> | |
213 </xml>) | |
214 | |
215 fun set ctl day = | |
216 minf <- rpc (monthInfo (day -- #Day)); | |
217 Basis.set ctl.Month minf; | |
218 Basis.set (ChangePoller.ctl ctl.Day) day | |
219 | |
220 fun value ctl = ChangePoller.value ctl.Day |