norcal 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/norcal +175 -201
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 628317b98eb4536e13c1199961ce51f89f246add68275897845fa08654060de1
|
|
4
|
+
data.tar.gz: 23ef85bb70d9342b372fb443b6ab26a3664c7b39b69ec479bbc54fe7a10c6423
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a23ac49843b37ed383b08d6215a5a9f4f24416471539654053abd2574c4d4f5aa46e714d56d6f78a50a14a9e43d17652d08173d2cb08ddb6a584e8d2a6515432
|
|
7
|
+
data.tar.gz: d4392d35c14eda23aebf58669e627c908c754cda781cdba6d61f72e4dc1a9b135ed56c8ff387683bf4f3e2c919c512ed44c839aae7d2c6901f773f611e5281f7
|
data/README.md
CHANGED
data/bin/norcal
CHANGED
|
@@ -170,56 +170,86 @@ def make_fonts
|
|
|
170
170
|
}
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
-
root = TkRoot.new do
|
|
173
|
+
$root = TkRoot.new do
|
|
174
174
|
title "norcal"
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
-
# --
|
|
177
|
+
# -- Widget tracking for in-place updates --------------------------------------
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
# All trackable widgets, grouped by update type.
|
|
180
|
+
# Labels store {w: widget, fg: theme_key, bg: theme_key, font: font_key}.
|
|
181
|
+
# Buttons store {w: widget, fg: theme_key, text_key: theme_key or nil, text: static}.
|
|
182
|
+
$w = { bg: [], border: [], sep: [], labels: [], texts: [], buttons: [] }
|
|
183
|
+
|
|
184
|
+
# Apply current theme + zoom to all tracked widgets (no widget recreation).
|
|
185
|
+
def restyle
|
|
180
186
|
t = theme
|
|
187
|
+
f = make_fonts
|
|
188
|
+
|
|
189
|
+
$root.configure(background: t[:bg])
|
|
190
|
+
$canvas.configure(background: t[:bg])
|
|
191
|
+
$scrollbar.configure(background: t[:bg], troughcolor: t[:bg])
|
|
192
|
+
$root.title = "Kalender #{$year}"
|
|
193
|
+
|
|
194
|
+
$w[:bg].each { |w| w.configure(background: t[:bg]) }
|
|
195
|
+
$w[:border].each { |w| w.configure(background: t[:bg], highlightbackground: t[:border]) }
|
|
196
|
+
$w[:sep].each { |w| w.configure(background: t[:sep]) }
|
|
197
|
+
|
|
198
|
+
$w[:labels].each do |e|
|
|
199
|
+
e[:w].configure(foreground: t[e[:fg]], background: t[e[:bg]], font: f[e[:font]])
|
|
200
|
+
end
|
|
181
201
|
|
|
202
|
+
$w[:texts].each do |tw|
|
|
203
|
+
tab = [65 + $zoom * 4, 40].max
|
|
204
|
+
tw.configure(background: t[:bg], font: f[:note], tabs: tab.to_s)
|
|
205
|
+
tw.tag_configure('dt', foreground: t[:fg], font: f[:note_b])
|
|
206
|
+
tw.tag_configure('dtr', foreground: t[:red], font: f[:note_b])
|
|
207
|
+
tw.tag_configure('desc', foreground: t[:desc_fg], font: f[:note_d])
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
$w[:buttons].each do |e|
|
|
211
|
+
text = e[:text_key] ? t[e[:text_key]] : e[:text]
|
|
212
|
+
e[:w].configure(text: text, font: f[:btn], foreground: t[e[:fg]],
|
|
213
|
+
background: t[:bg], activebackground: t[:bg], activeforeground: t[:fg])
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# -- Build calendar (one-time per year) ----------------------------------------
|
|
218
|
+
|
|
219
|
+
def build_month(parent, year, month, holidays)
|
|
182
220
|
first = Date.new(year, month, 1)
|
|
183
221
|
last_day = Date.new(year, month, -1)
|
|
184
222
|
monday = first - (first.cwday - 1)
|
|
185
223
|
today = Date.today
|
|
186
224
|
|
|
187
|
-
outer = TkFrame.new(parent) {
|
|
188
|
-
|
|
189
|
-
highlightbackground t[:border]; highlightthickness 0
|
|
190
|
-
}
|
|
225
|
+
outer = TkFrame.new(parent) { borderwidth 1; relief 'solid'; highlightthickness 0 }
|
|
226
|
+
$w[:border] << outer
|
|
191
227
|
|
|
192
|
-
grid = TkFrame.new(outer) {
|
|
228
|
+
grid = TkFrame.new(outer) { padx 6; pady 4 }
|
|
229
|
+
$w[:bg] << grid
|
|
193
230
|
grid.pack
|
|
194
231
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}.grid(row: 0, column: 0, columnspan: 8, pady: [0, 2])
|
|
232
|
+
hdr = TkLabel.new(grid) { text MONTHS_NO[month - 1] }
|
|
233
|
+
$w[:labels] << { w: hdr, fg: :fg, bg: :bg, font: :mheader }
|
|
234
|
+
hdr.grid(row: 0, column: 0, columnspan: 8, pady: [0, 2])
|
|
199
235
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}.grid(row: 1, column: 0, sticky: 'e', padx: [0, 4])
|
|
236
|
+
uke = TkLabel.new(grid) { text 'Uke' }
|
|
237
|
+
$w[:labels] << { w: uke, fg: :dkgray, bg: :bg, font: :cal }
|
|
238
|
+
uke.grid(row: 1, column: 0, sticky: 'e', padx: [0, 4])
|
|
204
239
|
|
|
205
240
|
DAYS_NO.each_with_index do |d, i|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
end
|
|
211
|
-
TkLabel.new(grid) {
|
|
212
|
-
text d; font f[:cal_bold]; foreground fg; background t[:bg]
|
|
213
|
-
}.grid(row: 1, column: i + 1, sticky: 'e')
|
|
241
|
+
fg_key = case i when 5 then :gray when 6 then :red else :fg end
|
|
242
|
+
lbl = TkLabel.new(grid) { text d }
|
|
243
|
+
$w[:labels] << { w: lbl, fg: fg_key, bg: :bg, font: :cal_bold }
|
|
244
|
+
lbl.grid(row: 1, column: i + 1, sticky: 'e')
|
|
214
245
|
end
|
|
215
246
|
|
|
216
|
-
# Week rows
|
|
217
247
|
row = 2
|
|
218
248
|
cur = monday
|
|
219
249
|
while cur <= last_day
|
|
220
|
-
TkLabel.new(grid) {
|
|
221
|
-
|
|
222
|
-
|
|
250
|
+
wk = TkLabel.new(grid) { text cur.cweek.to_s }
|
|
251
|
+
$w[:labels] << { w: wk, fg: :dkgray, bg: :bg, font: :cal }
|
|
252
|
+
wk.grid(row: row, column: 0, sticky: 'e', padx: [0, 4])
|
|
223
253
|
|
|
224
254
|
7.times do |i|
|
|
225
255
|
day = cur + i
|
|
@@ -227,263 +257,207 @@ def render_month(parent, year, month, holidays, f)
|
|
|
227
257
|
is_red = day.cwday == 7 || holidays.key?(day)
|
|
228
258
|
is_sat = day.cwday == 6 && !holidays.key?(day)
|
|
229
259
|
is_today = day == today
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
TkLabel.new(grid) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
foreground fg
|
|
238
|
-
background(is_today ? t[:today_bg] : t[:bg])
|
|
239
|
-
}.grid(row: row, column: i + 1, sticky: 'e')
|
|
260
|
+
fg_key = is_red ? :red : (is_sat ? :gray : :fg)
|
|
261
|
+
bg_key = is_today ? :today_bg : :bg
|
|
262
|
+
font_key = is_today ? :cal_bold : :cal
|
|
263
|
+
|
|
264
|
+
lbl = TkLabel.new(grid) { text day.day.to_s }
|
|
265
|
+
$w[:labels] << { w: lbl, fg: fg_key, bg: bg_key, font: font_key }
|
|
266
|
+
lbl.grid(row: row, column: i + 1, sticky: 'e')
|
|
240
267
|
end
|
|
241
268
|
end
|
|
242
269
|
row += 1
|
|
243
270
|
cur += 7
|
|
244
271
|
end
|
|
245
272
|
|
|
246
|
-
# Pad empty rows so all months have the same height (max 6 week rows)
|
|
247
273
|
while row < 8
|
|
248
|
-
TkLabel.new(grid) { text ' '
|
|
249
|
-
|
|
274
|
+
pad = TkLabel.new(grid) { text ' ' }
|
|
275
|
+
$w[:labels] << { w: pad, fg: :fg, bg: :bg, font: :cal }
|
|
276
|
+
pad.grid(row: row, column: 0)
|
|
250
277
|
row += 1
|
|
251
278
|
end
|
|
252
279
|
|
|
253
|
-
# Uniform column widths
|
|
254
280
|
(1..7).each { |c| grid.grid_columnconfigure(c, uniform: 'day', minsize: 22) }
|
|
255
|
-
|
|
256
281
|
outer
|
|
257
282
|
end
|
|
258
283
|
|
|
259
|
-
def
|
|
260
|
-
t = theme
|
|
284
|
+
def build_notable(parent, year)
|
|
261
285
|
dates = notable_dates(year)
|
|
262
|
-
|
|
263
|
-
# Split into 3 roughly-equal columns
|
|
264
286
|
third = (dates.size / 3.0).ceil
|
|
265
|
-
cols
|
|
287
|
+
cols = dates.each_slice(third).to_a
|
|
266
288
|
|
|
267
|
-
outer = TkFrame.new(parent) {
|
|
268
|
-
|
|
269
|
-
highlightbackground t[:border]; highlightthickness 0
|
|
270
|
-
}
|
|
289
|
+
outer = TkFrame.new(parent) { borderwidth 1; relief 'solid'; highlightthickness 0 }
|
|
290
|
+
$w[:border] << outer
|
|
271
291
|
|
|
272
|
-
inner = TkFrame.new(outer)
|
|
292
|
+
inner = TkFrame.new(outer)
|
|
293
|
+
$w[:bg] << inner
|
|
273
294
|
inner.pack(padx: 5, pady: 5, fill: 'both')
|
|
274
295
|
|
|
275
296
|
cols.each_with_index do |entries, ci|
|
|
276
297
|
tw = TkText.new(inner) {
|
|
277
298
|
width 1; height(entries.size + 4)
|
|
278
|
-
|
|
279
|
-
wrap 'word';
|
|
280
|
-
cursor ''; insertwidth 0
|
|
281
|
-
tabs '65'
|
|
299
|
+
borderwidth 0; highlightthickness 0
|
|
300
|
+
wrap 'word'; padx 3; pady 2; cursor ''; insertwidth 0
|
|
282
301
|
}
|
|
283
|
-
|
|
284
|
-
tw.tag_configure('dtr', foreground: t[:red], font: f[:note_b])
|
|
285
|
-
tw.tag_configure('desc', foreground: t[:desc_fg], font: f[:note_d])
|
|
302
|
+
$w[:texts] << tw
|
|
286
303
|
|
|
287
304
|
entries.each_with_index do |(date, desc, is_red), i|
|
|
288
305
|
dtag = is_red ? 'dtr' : 'dt'
|
|
289
|
-
|
|
290
|
-
tw.insert('end', date_str, dtag)
|
|
306
|
+
tw.insert('end', "#{date.day}. #{MABBR_NO[date.month - 1]}", dtag)
|
|
291
307
|
tw.insert('end', "\t")
|
|
292
308
|
tw.insert('end', desc, 'desc')
|
|
293
309
|
tw.insert('end', "\n") unless i == entries.size - 1
|
|
294
310
|
end
|
|
295
|
-
|
|
296
311
|
tw.state 'disabled'
|
|
297
312
|
tw.grid(row: 0, column: ci * 2, sticky: 'nsew', padx: [0, 2])
|
|
298
313
|
inner.grid_columnconfigure(ci * 2, weight: 1, uniform: 'notecol')
|
|
299
314
|
inner.grid_rowconfigure(0, weight: 1)
|
|
300
315
|
|
|
301
|
-
# vertical separator between columns (except after last)
|
|
302
316
|
if ci < cols.size - 1
|
|
303
|
-
sep = TkFrame.new(inner) {
|
|
317
|
+
sep = TkFrame.new(inner) { width 1 }
|
|
318
|
+
$w[:sep] << sep
|
|
304
319
|
sep.grid(row: 0, column: ci * 2 + 1, sticky: 'ns', padx: 4)
|
|
305
320
|
end
|
|
306
321
|
end
|
|
307
|
-
|
|
308
322
|
outer
|
|
309
323
|
end
|
|
310
324
|
|
|
311
|
-
def
|
|
312
|
-
|
|
325
|
+
def build_buttons(parent)
|
|
326
|
+
btn_bar = TkFrame.new(parent)
|
|
327
|
+
$w[:bg] << btn_bar
|
|
328
|
+
btn_bar.grid(row: 5, column: 0, columnspan: 3, pady: [4, 0])
|
|
329
|
+
|
|
330
|
+
b1 = TkButton.new(btn_bar) { relief 'flat'; borderwidth 0; command { $dark = !$dark; restyle } }
|
|
331
|
+
$w[:buttons] << { w: b1, fg: :dkgray, text_key: :toggle_label, text: nil }
|
|
332
|
+
b1.pack(side: 'left', padx: 4)
|
|
333
|
+
|
|
334
|
+
b2 = TkButton.new(btn_bar) { relief 'flat'; borderwidth 0; command { $zoom += 1; restyle } }
|
|
335
|
+
$w[:buttons] << { w: b2, fg: :dkgray, text_key: nil, text: '+' }
|
|
336
|
+
b2.pack(side: 'left', padx: 2)
|
|
337
|
+
|
|
338
|
+
b3 = TkButton.new(btn_bar) { relief 'flat'; borderwidth 0; command { $zoom -= 1 if $zoom > -8; restyle } }
|
|
339
|
+
$w[:buttons] << { w: b3, fg: :dkgray, text_key: nil, text: "\u2013" }
|
|
340
|
+
b3.pack(side: 'left', padx: 2)
|
|
341
|
+
|
|
342
|
+
b4 = TkButton.new(btn_bar) { relief 'flat'; borderwidth 0; command { $fit.call(false) } }
|
|
343
|
+
$w[:buttons] << { w: b4, fg: :dkgray, text_key: nil, text: 'fit' }
|
|
344
|
+
b4.pack(side: 'left', padx: 2)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def build_all(year)
|
|
348
|
+
$w.each_value(&:clear)
|
|
349
|
+
$inner_frame&.destroy
|
|
350
|
+
|
|
313
351
|
holidays = red_days(year)
|
|
314
352
|
|
|
315
|
-
|
|
353
|
+
$inner_frame = TkFrame.new($canvas)
|
|
354
|
+
$w[:bg] << $inner_frame
|
|
355
|
+
|
|
356
|
+
title = TkLabel.new($inner_frame) { text year.to_s }
|
|
357
|
+
$w[:labels] << { w: title, fg: :title_fg, bg: :bg, font: :title }
|
|
358
|
+
title.pack(pady: [8, 2])
|
|
359
|
+
|
|
360
|
+
frame = TkFrame.new($inner_frame)
|
|
361
|
+
$w[:bg] << frame
|
|
362
|
+
frame.pack(padx: 10, pady: [2, 8])
|
|
316
363
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
grid_f.pack
|
|
364
|
+
grid_f = TkFrame.new(frame)
|
|
365
|
+
$w[:bg] << grid_f
|
|
366
|
+
grid_f.pack
|
|
320
367
|
|
|
321
368
|
12.times do |idx|
|
|
322
|
-
|
|
323
|
-
|
|
369
|
+
build_month(grid_f, year, idx + 1, holidays)
|
|
370
|
+
.grid(row: idx / 3, column: idx % 3, padx: 3, pady: 3, sticky: 'nsew')
|
|
324
371
|
end
|
|
325
372
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
note_f.grid(row: 4, column: 0, columnspan: 3, padx: 3, pady: [4, 0], sticky: 'ew')
|
|
373
|
+
build_notable(grid_f, year)
|
|
374
|
+
.grid(row: 4, column: 0, columnspan: 3, padx: 3, pady: [4, 0], sticky: 'ew')
|
|
329
375
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
btn_bar.grid(row: 5, column: 0, columnspan: 3, pady: [4, 0])
|
|
376
|
+
build_buttons(grid_f)
|
|
377
|
+
3.times { |c| grid_f.grid_columnconfigure(c, weight: 1) }
|
|
333
378
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
foreground t[:dkgray]; background t[:bg]
|
|
338
|
-
activebackground t[:bg]; activeforeground t[:fg]
|
|
339
|
-
command { $dark = !$dark; $rebuild&.call }
|
|
340
|
-
}.pack(side: 'left', padx: 4)
|
|
341
|
-
|
|
342
|
-
TkButton.new(btn_bar) {
|
|
343
|
-
text '+'; font f[:btn]
|
|
344
|
-
relief 'flat'; borderwidth 0
|
|
345
|
-
foreground t[:dkgray]; background t[:bg]
|
|
346
|
-
activebackground t[:bg]; activeforeground t[:fg]
|
|
347
|
-
command { $zoom += 1; $rebuild&.call }
|
|
348
|
-
}.pack(side: 'left', padx: 2)
|
|
349
|
-
|
|
350
|
-
TkButton.new(btn_bar) {
|
|
351
|
-
text "\u2013"; font f[:btn] # en-dash for minus
|
|
352
|
-
relief 'flat'; borderwidth 0
|
|
353
|
-
foreground t[:dkgray]; background t[:bg]
|
|
354
|
-
activebackground t[:bg]; activeforeground t[:fg]
|
|
355
|
-
command { $zoom -= 1 if $zoom > -8; $rebuild&.call }
|
|
356
|
-
}.pack(side: 'left', padx: 2)
|
|
357
|
-
|
|
358
|
-
TkButton.new(btn_bar) {
|
|
359
|
-
text 'fit'; font f[:btn]
|
|
360
|
-
relief 'flat'; borderwidth 0
|
|
361
|
-
foreground t[:dkgray]; background t[:bg]
|
|
362
|
-
activebackground t[:bg]; activeforeground t[:fg]
|
|
363
|
-
command { $fit&.call }
|
|
364
|
-
}.pack(side: 'left', padx: 2)
|
|
379
|
+
# Embed in canvas
|
|
380
|
+
$canvas.delete('all')
|
|
381
|
+
$canvas_win = $canvas.create(:window, 0, 0, window: $inner_frame, anchor: 'nw')
|
|
365
382
|
|
|
366
|
-
|
|
383
|
+
center = proc {
|
|
384
|
+
cw = $canvas.winfo_width; ch = $canvas.winfo_height
|
|
385
|
+
fw = $inner_frame.winfo_reqwidth; fh = $inner_frame.winfo_reqheight
|
|
386
|
+
$canvas.coords($canvas_win, [(cw - fw) / 2, 0].max, [(ch - fh) / 2, 0].max)
|
|
387
|
+
$canvas.configure(scrollregion: "0 0 #{[cw, fw].max} #{[ch, fh].max}")
|
|
388
|
+
}
|
|
389
|
+
$inner_frame.bind('Configure', center)
|
|
390
|
+
$canvas.bind('Configure', center)
|
|
367
391
|
|
|
368
|
-
|
|
392
|
+
restyle
|
|
369
393
|
end
|
|
370
394
|
|
|
371
395
|
# -- Scrollable wrapper --------------------------------------------------------
|
|
372
396
|
|
|
373
|
-
$scrollbar = TkScrollbar.new(root)
|
|
374
|
-
$canvas = TkCanvas.new(root) {
|
|
375
|
-
highlightthickness 0; borderwidth 0
|
|
376
|
-
}
|
|
397
|
+
$scrollbar = TkScrollbar.new($root)
|
|
398
|
+
$canvas = TkCanvas.new($root) { highlightthickness 0; borderwidth 0 }
|
|
377
399
|
$canvas.configure(yscrollcommand: proc { |*a| $scrollbar.set(*a) })
|
|
378
400
|
$scrollbar.command(proc { |*a| $canvas.yview(*a) })
|
|
379
|
-
|
|
380
401
|
$scrollbar.pack(side: 'right', fill: 'y')
|
|
381
402
|
$canvas.pack(side: 'left', fill: 'both', expand: true)
|
|
382
403
|
|
|
383
|
-
#
|
|
384
|
-
root.bind('MouseWheel') { |e| $canvas.yview_scroll(-e.delta / 120, 'units') }
|
|
385
|
-
root.bind('Button-4') { $canvas.yview_scroll(-3, 'units') }
|
|
386
|
-
root.bind('Button-5') { $canvas.yview_scroll(3, 'units') }
|
|
404
|
+
# Scrolling
|
|
405
|
+
$root.bind('MouseWheel') { |e| $canvas.yview_scroll(-e.delta / 120, 'units') }
|
|
406
|
+
$root.bind('Button-4') { $canvas.yview_scroll(-3, 'units') }
|
|
407
|
+
$root.bind('Button-5') { $canvas.yview_scroll(3, 'units') }
|
|
387
408
|
|
|
388
|
-
# Keyboard zoom
|
|
389
|
-
root.bind('Control-plus') { $zoom += 1;
|
|
390
|
-
root.bind('Control-equal') { $zoom += 1;
|
|
391
|
-
root.bind('Control-minus') { $zoom -= 1 if $zoom > -8;
|
|
409
|
+
# Keyboard zoom
|
|
410
|
+
$root.bind('Control-plus') { $zoom += 1; restyle }
|
|
411
|
+
$root.bind('Control-equal') { $zoom += 1; restyle }
|
|
412
|
+
$root.bind('Control-minus') { $zoom -= 1 if $zoom > -8; restyle }
|
|
392
413
|
|
|
393
414
|
# Ctrl+mouse wheel zoom
|
|
394
|
-
root.bind('Control-MouseWheel') { |e|
|
|
415
|
+
$root.bind('Control-MouseWheel') { |e|
|
|
395
416
|
if e.delta > 0 then $zoom += 1 else $zoom -= 1 if $zoom > -8 end
|
|
396
|
-
|
|
417
|
+
restyle
|
|
397
418
|
}
|
|
398
|
-
root.bind('Control-Button-4') { $zoom += 1;
|
|
399
|
-
root.bind('Control-Button-5') { $zoom -= 1 if $zoom > -8;
|
|
400
|
-
|
|
401
|
-
# -- Build & rebuild -----------------------------------------------------------
|
|
402
|
-
|
|
403
|
-
$inner_frame = nil
|
|
404
|
-
$rebuild = nil
|
|
405
|
-
|
|
406
|
-
$rebuild = proc {
|
|
407
|
-
t = theme
|
|
408
|
-
f = make_fonts
|
|
409
|
-
|
|
410
|
-
$inner_frame&.destroy
|
|
411
|
-
root.configure(background: t[:bg])
|
|
412
|
-
$canvas.configure(background: t[:bg])
|
|
413
|
-
$scrollbar.configure(background: t[:bg], troughcolor: t[:bg])
|
|
414
|
-
root.title = "norcal - #{$year}"
|
|
415
|
-
|
|
416
|
-
$inner_frame = TkFrame.new($canvas) { background t[:bg] }
|
|
417
|
-
|
|
418
|
-
# Title
|
|
419
|
-
TkLabel.new($inner_frame) {
|
|
420
|
-
text $year.to_s; font f[:title]; foreground t[:title_fg]; background t[:bg]
|
|
421
|
-
}.pack(pady: [8, 2])
|
|
422
|
-
|
|
423
|
-
# Calendar content
|
|
424
|
-
build_calendar($inner_frame, $year, f).pack(fill: 'both', expand: true)
|
|
419
|
+
$root.bind('Control-Button-4') { $zoom += 1; restyle }
|
|
420
|
+
$root.bind('Control-Button-5') { $zoom -= 1 if $zoom > -8; restyle }
|
|
425
421
|
|
|
426
|
-
|
|
427
|
-
$canvas.delete('all')
|
|
428
|
-
$canvas_win = $canvas.create(:window, 0, 0, window: $inner_frame, anchor: 'nw')
|
|
429
|
-
|
|
430
|
-
# Center content and update scroll region on resize
|
|
431
|
-
center_content = proc {
|
|
432
|
-
cw = $canvas.winfo_width
|
|
433
|
-
ch = $canvas.winfo_height
|
|
434
|
-
fw = $inner_frame.winfo_reqwidth
|
|
435
|
-
fh = $inner_frame.winfo_reqheight
|
|
436
|
-
x = [(cw - fw) / 2, 0].max
|
|
437
|
-
y = [(ch - fh) / 2, 0].max
|
|
438
|
-
$canvas.coords($canvas_win, x, y)
|
|
439
|
-
$canvas.configure(scrollregion: "#{0} #{0} #{[cw, fw].max} #{[ch, fh].max}")
|
|
440
|
-
}
|
|
422
|
+
# -- Fit to window -------------------------------------------------------------
|
|
441
423
|
|
|
442
|
-
|
|
443
|
-
$
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
# Fit-to-window: zoom so content fills available height, then resize window
|
|
447
|
-
$fit = proc {
|
|
448
|
-
# Measure current content height and extrapolate target zoom
|
|
424
|
+
$fit = proc { |resize_window|
|
|
425
|
+
target_h = resize_window ? $root.winfo_screenheight - 80 : $root.winfo_height
|
|
449
426
|
cur_h = $inner_frame.winfo_reqheight.to_f
|
|
450
|
-
|
|
451
|
-
base_avg = 13.0 # average base font size at zoom 0
|
|
427
|
+
base_avg = 13.0
|
|
452
428
|
|
|
453
|
-
if cur_h > 0 &&
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
target_font = cur_font * (screen_h / cur_h)
|
|
429
|
+
if cur_h > 0 && target_h > 0
|
|
430
|
+
cur_font = base_avg + $zoom
|
|
431
|
+
target_font = cur_font * (target_h / cur_h)
|
|
457
432
|
$zoom = (target_font - base_avg).floor
|
|
458
433
|
$zoom = [$zoom, -8].max
|
|
459
434
|
end
|
|
460
435
|
|
|
461
|
-
|
|
436
|
+
restyle
|
|
462
437
|
Tk.update_idletasks
|
|
463
438
|
|
|
464
|
-
|
|
465
|
-
if $inner_frame.winfo_reqheight > screen_h
|
|
439
|
+
if $inner_frame.winfo_reqheight > target_h
|
|
466
440
|
$zoom -= 1
|
|
467
|
-
|
|
441
|
+
restyle
|
|
468
442
|
Tk.update_idletasks
|
|
469
443
|
end
|
|
470
444
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
root.geometry("#{w}x#{h}+#{x}+0")
|
|
445
|
+
if resize_window
|
|
446
|
+
cw = $inner_frame.winfo_reqwidth + 20
|
|
447
|
+
ch = $inner_frame.winfo_reqheight
|
|
448
|
+
sw = $root.winfo_screenwidth
|
|
449
|
+
mh = $root.winfo_screenheight - 50
|
|
450
|
+
w = [cw, sw].min; h = [ch, mh].min
|
|
451
|
+
$root.geometry("#{w}x#{h}+#{(sw - w) / 2}+0")
|
|
452
|
+
end
|
|
480
453
|
}
|
|
481
454
|
|
|
482
|
-
#
|
|
483
|
-
|
|
484
|
-
$
|
|
455
|
+
# -- Startup -------------------------------------------------------------------
|
|
456
|
+
|
|
457
|
+
$root.withdraw
|
|
458
|
+
build_all($year)
|
|
485
459
|
Tk.update_idletasks
|
|
486
|
-
$fit.call
|
|
487
|
-
root.deiconify
|
|
460
|
+
$fit.call(true)
|
|
461
|
+
$root.deiconify
|
|
488
462
|
|
|
489
463
|
Tk.mainloop
|