norcal 1.0.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 +7 -0
- data/LICENSE +15 -0
- data/README.md +58 -0
- data/bin/norcal +369 -0
- metadata +57 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: be5696e7434a165a2ef025c8152ddd359795d5cf6e1a42d6dae6a32c2f636e90
|
|
4
|
+
data.tar.gz: fd6398b5697251b1c28c1c0b20c8fb3d99bc6b08980004612d37c94ea00e3469
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f8e7e3cf2c3e977ba03769fa6c215c18e2ff5fe13f7439b36ed18108f4db5d7ce6d12ef0cb9076ff88b70f923a30e2815b422d79a4da0de80a7de0d4a8192abb
|
|
7
|
+
data.tar.gz: d2f1258004cd2b3b3a0fbc0a501fd94ef6bd0b5df63145747c899d68916718ffa9fcdb181d3ea46b7355b4672df1c8056f053b5988d2c7632f81a71093c2e42a
|
data/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 baosen <chibaosen@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# norcal
|
|
2
|
+
|
|
3
|
+
A local Norwegian desktop calendar.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
First, install the Tcl/Tk system libraries:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Debian/Ubuntu
|
|
13
|
+
sudo apt install libtk8.6 tk8.6-dev tcl8.6-dev
|
|
14
|
+
|
|
15
|
+
# Fedora
|
|
16
|
+
sudo dnf install tk-devel tcl-devel
|
|
17
|
+
|
|
18
|
+
# macOS
|
|
19
|
+
brew install tcl-tk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### From RubyGems
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
gem install norcal
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### From source
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
git clone https://github.com/baosen/norcal.git
|
|
32
|
+
cd norcal
|
|
33
|
+
gem build norcal.gemspec
|
|
34
|
+
gem install norcal-1.0.0.gem
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
norcal # current year, dark mode
|
|
41
|
+
norcal 2026 # specific year
|
|
42
|
+
norcal --light # light mode
|
|
43
|
+
norcal --light 2026 # light mode, specific year
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- 12-month grid (4x3) with Norwegian month and day names
|
|
49
|
+
- ISO week numbers, Monday-first weeks
|
|
50
|
+
- Sundays and public holidays in red, Saturdays in gray
|
|
51
|
+
- Easter-based movable holidays (Computus algorithm)
|
|
52
|
+
- Notable dates: royal birthdays, Samefolkets dag, Morsdag, Farsdag, solverv, sommertid, and more
|
|
53
|
+
- Today highlighted with yellow background
|
|
54
|
+
- Dark mode (default) and light mode, with toggle button
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
[ISC](LICENSE)
|
data/bin/norcal
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# norcal - Norwegian desktop calendar
|
|
3
|
+
|
|
4
|
+
require 'date'
|
|
5
|
+
require 'tk'
|
|
6
|
+
|
|
7
|
+
# Ruby 4.0 froze all string literals by default, which breaks the tk gem's
|
|
8
|
+
# internal _toUTF8/_fromUTF8 helpers (they call force_encoding on the return
|
|
9
|
+
# value of the C-level converter). Wrapping those C methods to dup frozen
|
|
10
|
+
# return values is the least-invasive fix.
|
|
11
|
+
class TclTkIp
|
|
12
|
+
%i[__toUTF8 __fromUTF8].each do |m|
|
|
13
|
+
orig = :"#{m}_orig"
|
|
14
|
+
alias_method orig, m
|
|
15
|
+
define_method(m) { |*a| (r = send(orig, *a)).frozen? ? +r : r }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# -- Norwegian locale constants ------------------------------------------------
|
|
20
|
+
|
|
21
|
+
MONTHS_NO = %w[
|
|
22
|
+
Januar Februar Mars April Mai Juni
|
|
23
|
+
Juli August September Oktober November Desember
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
DAYS_NO = %w[Ma Ti On To Fr Lø Sø].freeze
|
|
27
|
+
MABBR_NO = %w[jan feb mar apr mai jun jul aug sep okt nov des].freeze
|
|
28
|
+
|
|
29
|
+
# -- Date helpers --------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
# Easter Sunday via the Anonymous Gregorian algorithm (Computus)
|
|
32
|
+
def easter(year)
|
|
33
|
+
a = year % 19
|
|
34
|
+
b, c = year.divmod(100)
|
|
35
|
+
d, e = b.divmod(4)
|
|
36
|
+
f = (b + 8) / 25
|
|
37
|
+
g = (b - f + 1) / 3
|
|
38
|
+
h = (19 * a + b - d - g + 15) % 30
|
|
39
|
+
i, k = c.divmod(4)
|
|
40
|
+
l = (32 + 2 * e + 2 * i - h - k) % 7
|
|
41
|
+
m = (a + 11 * h + 22 * l) / 451
|
|
42
|
+
month, day = (h + l - 7 * m + 114).divmod(31)
|
|
43
|
+
Date.new(year, month, day + 1)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# n-th occurrence of a weekday (cwday 1=Mon..7=Sun) in a given month
|
|
47
|
+
def nth_wday(year, month, wday, n)
|
|
48
|
+
first = Date.new(year, month, 1)
|
|
49
|
+
first + ((wday - first.cwday) % 7) + (n - 1) * 7
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Last occurrence of a weekday in a given month
|
|
53
|
+
def last_wday(year, month, wday)
|
|
54
|
+
last = Date.new(year, month, -1)
|
|
55
|
+
last - ((last.cwday - wday) % 7)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# -- Norwegian holidays --------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
# Public holidays (røde dager) - official days off
|
|
61
|
+
def red_days(year)
|
|
62
|
+
e = easter(year)
|
|
63
|
+
{
|
|
64
|
+
Date.new(year, 1, 1) => "Nyttårsdag",
|
|
65
|
+
e - 3 => "Skjærtorsdag",
|
|
66
|
+
e - 2 => "Langfredag",
|
|
67
|
+
e => "1. påskedag",
|
|
68
|
+
e + 1 => "2. påskedag",
|
|
69
|
+
Date.new(year, 5, 1) => "Off. høgtidsdag",
|
|
70
|
+
e + 39 => "Kr. Himmelfartsdag",
|
|
71
|
+
Date.new(year, 5, 17) => "Grunnlovsdag",
|
|
72
|
+
e + 49 => "1. pinsedag",
|
|
73
|
+
e + 50 => "2. pinsedag",
|
|
74
|
+
Date.new(year, 12, 25) => "1. juledag",
|
|
75
|
+
Date.new(year, 12, 26) => "2. juledag",
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Notable dates: [date, description, show_date_in_red?]
|
|
80
|
+
def notable_dates(year)
|
|
81
|
+
e = easter(year)
|
|
82
|
+
morsdag = nth_wday(year, 2, 7, 2)
|
|
83
|
+
farsdag = nth_wday(year, 11, 7, 2)
|
|
84
|
+
sommer_start = last_wday(year, 3, 7)
|
|
85
|
+
sommer_slutt = last_wday(year, 10, 7)
|
|
86
|
+
fastelavn = e - 49
|
|
87
|
+
nov27 = Date.new(year, 11, 27)
|
|
88
|
+
advent1 = nov27 + ((7 - nov27.cwday) % 7)
|
|
89
|
+
|
|
90
|
+
[
|
|
91
|
+
[Date.new(year, 1, 1), "Nyttårsdag", true],
|
|
92
|
+
[Date.new(year, 1, 21), "Prinsesse Ingrid Alexandra, #{year - 2004} år", false],
|
|
93
|
+
[Date.new(year, 2, 6), "Samefolkets dag", false],
|
|
94
|
+
[morsdag, "Morsdag", false],
|
|
95
|
+
[Date.new(year, 2, 14), "St. Valentines dag", false],
|
|
96
|
+
[fastelavn, "Fastelavn", false],
|
|
97
|
+
[Date.new(year, 2, 21), "Kong Harald, #{year - 1937} år", false],
|
|
98
|
+
[Date.new(year, 3, 20), "Vårjevndøgn", false],
|
|
99
|
+
[sommer_start, "Sommertid start", false],
|
|
100
|
+
[e - 7, "Palmesøndag", true],
|
|
101
|
+
[e - 3, "Skjærtorsdag", true],
|
|
102
|
+
[e - 2, "Langfredag", true],
|
|
103
|
+
[e, "1. påskedag", true],
|
|
104
|
+
[e + 1, "2. påskedag", true],
|
|
105
|
+
[Date.new(year, 5, 1), "Off. høgtidsdag", true],
|
|
106
|
+
[Date.new(year, 5, 8), "Frigjøringsdag 1945", false],
|
|
107
|
+
[e + 39, "Kr. Himmelfartsdag", true],
|
|
108
|
+
[Date.new(year, 5, 17), "Grunnlovsdag 1814", true],
|
|
109
|
+
[e + 49, "1. pinsedag", true],
|
|
110
|
+
[e + 50, "2. pinsedag", true],
|
|
111
|
+
[Date.new(year, 6, 7), "Unionsoppløsning 1905", false],
|
|
112
|
+
[Date.new(year, 6, 21), "Sommersolverv", false],
|
|
113
|
+
[Date.new(year, 6, 23), "Jonsokaften", false],
|
|
114
|
+
[Date.new(year, 7, 4), "Dronning Sonja, #{year - 1937} år", false],
|
|
115
|
+
[Date.new(year, 7, 20), "Kronprins Haakon, #{year - 1973} år", false],
|
|
116
|
+
[Date.new(year, 7, 29), "Olsokdagen", false],
|
|
117
|
+
[Date.new(year, 8, 19), "Kronprinsesse Mette-Marit, #{year - 1973} år", false],
|
|
118
|
+
[Date.new(year, 9, 23), "Høstjevndøgn", false],
|
|
119
|
+
[sommer_slutt, "Sommertid slutt", false],
|
|
120
|
+
[Date.new(year, 10, 31), "Halloween", false],
|
|
121
|
+
[Date.new(year, 11, 1), "Allehelgensdag", false],
|
|
122
|
+
[farsdag, "Farsdag", false],
|
|
123
|
+
[advent1, "1. søndag i advent", false],
|
|
124
|
+
[Date.new(year, 12, 13), "Luciadagen", false],
|
|
125
|
+
[Date.new(year, 12, 21), "Vintersolverv", false],
|
|
126
|
+
[Date.new(year, 12, 25), "1. juledag", true],
|
|
127
|
+
[Date.new(year, 12, 26), "2. juledag", true],
|
|
128
|
+
].sort_by { |d, _, _| d }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# -- GUI -----------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
THEME = {
|
|
134
|
+
light: {
|
|
135
|
+
bg: '#ffffff', fg: '#000000', title_fg: '#333333',
|
|
136
|
+
red: '#cc0000', gray: '#999999', dkgray: '#444444',
|
|
137
|
+
border: '#777777', today_bg: '#ffed6f', desc_fg: '#333333',
|
|
138
|
+
sep: '#777777', toggle_label: 'dark',
|
|
139
|
+
},
|
|
140
|
+
dark: {
|
|
141
|
+
bg: '#1e1e1e', fg: '#d4d4d4', title_fg: '#cccccc',
|
|
142
|
+
red: '#ff6b6b', gray: '#777777', dkgray: '#999999',
|
|
143
|
+
border: '#555555', today_bg: '#4a4400', desc_fg: '#aaaaaa',
|
|
144
|
+
sep: '#555555', toggle_label: 'light',
|
|
145
|
+
},
|
|
146
|
+
}.freeze
|
|
147
|
+
|
|
148
|
+
# Parse arguments: optional --light flag and optional year (dark is default)
|
|
149
|
+
$dark = ARGV.delete('--light') ? false : true
|
|
150
|
+
$year = (ARGV[0] || Date.today.year).to_i
|
|
151
|
+
|
|
152
|
+
def theme
|
|
153
|
+
THEME[$dark ? :dark : :light]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
root = TkRoot.new do
|
|
157
|
+
title "norcal"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Fonts
|
|
161
|
+
ft_title = TkFont.new(family: 'Noto Serif', size: 22, weight: 'bold')
|
|
162
|
+
ft_mheader = TkFont.new(family: 'Noto Serif', size: 13, weight: 'bold')
|
|
163
|
+
ft_cal = TkFont.new(family: 'Noto Serif', size: 12)
|
|
164
|
+
ft_cal_bold = TkFont.new(family: 'Noto Serif', size: 12, weight: 'bold')
|
|
165
|
+
ft_note = TkFont.new(family: 'Noto Serif', size: 11)
|
|
166
|
+
ft_note_b = TkFont.new(family: 'Noto Serif', size: 11, weight: 'bold')
|
|
167
|
+
ft_note_d = TkFont.new(family: 'Noto Serif', size: 11)
|
|
168
|
+
ft_toggle = TkFont.new(family: 'Noto Serif', size: 9)
|
|
169
|
+
|
|
170
|
+
# -- Calendar content ----------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
def render_month(parent, year, month, holidays, fonts)
|
|
173
|
+
ft_mheader, ft_cal, ft_cal_bold = fonts
|
|
174
|
+
t = theme
|
|
175
|
+
|
|
176
|
+
first = Date.new(year, month, 1)
|
|
177
|
+
last_day = Date.new(year, month, -1)
|
|
178
|
+
monday = first - (first.cwday - 1)
|
|
179
|
+
today = Date.today
|
|
180
|
+
|
|
181
|
+
outer = TkFrame.new(parent) {
|
|
182
|
+
borderwidth 1; relief 'solid'; background t[:bg]
|
|
183
|
+
highlightbackground t[:border]; highlightthickness 0
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
grid = TkFrame.new(outer) { background t[:bg]; padx 6; pady 4 }
|
|
187
|
+
grid.pack
|
|
188
|
+
|
|
189
|
+
# Month name header
|
|
190
|
+
TkLabel.new(grid) {
|
|
191
|
+
text MONTHS_NO[month - 1]; font ft_mheader; foreground t[:fg]; background t[:bg]
|
|
192
|
+
}.grid(row: 0, column: 0, columnspan: 8, pady: [0, 2])
|
|
193
|
+
|
|
194
|
+
# Day-name headers
|
|
195
|
+
TkLabel.new(grid) {
|
|
196
|
+
text 'Uke'; font ft_cal; foreground t[:dkgray]; background t[:bg]
|
|
197
|
+
}.grid(row: 1, column: 0, sticky: 'e', padx: [0, 4])
|
|
198
|
+
|
|
199
|
+
DAYS_NO.each_with_index do |d, i|
|
|
200
|
+
fg = case i
|
|
201
|
+
when 5 then t[:gray]
|
|
202
|
+
when 6 then t[:red]
|
|
203
|
+
else t[:fg]
|
|
204
|
+
end
|
|
205
|
+
TkLabel.new(grid) {
|
|
206
|
+
text d; font ft_cal_bold; foreground fg; background t[:bg]
|
|
207
|
+
}.grid(row: 1, column: i + 1, sticky: 'e')
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Week rows
|
|
211
|
+
row = 2
|
|
212
|
+
cur = monday
|
|
213
|
+
while cur <= last_day
|
|
214
|
+
TkLabel.new(grid) {
|
|
215
|
+
text cur.cweek.to_s; font ft_cal; foreground t[:dkgray]; background t[:bg]
|
|
216
|
+
}.grid(row: row, column: 0, sticky: 'e', padx: [0, 4])
|
|
217
|
+
|
|
218
|
+
7.times do |i|
|
|
219
|
+
day = cur + i
|
|
220
|
+
if day.month == month && day.year == year
|
|
221
|
+
is_red = day.cwday == 7 || holidays.key?(day)
|
|
222
|
+
is_sat = day.cwday == 6 && !holidays.key?(day)
|
|
223
|
+
is_today = day == today
|
|
224
|
+
fg = if is_red then t[:red]
|
|
225
|
+
elsif is_sat then t[:gray]
|
|
226
|
+
else t[:fg]
|
|
227
|
+
end
|
|
228
|
+
TkLabel.new(grid) {
|
|
229
|
+
text day.day.to_s
|
|
230
|
+
font(is_today ? ft_cal_bold : ft_cal)
|
|
231
|
+
foreground fg
|
|
232
|
+
background(is_today ? t[:today_bg] : t[:bg])
|
|
233
|
+
}.grid(row: row, column: i + 1, sticky: 'e')
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
row += 1
|
|
237
|
+
cur += 7
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Pad empty rows so all months have the same height (max 6 week rows)
|
|
241
|
+
while row < 8
|
|
242
|
+
TkLabel.new(grid) { text ' '; font ft_cal; background t[:bg] }
|
|
243
|
+
.grid(row: row, column: 0)
|
|
244
|
+
row += 1
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Uniform column widths
|
|
248
|
+
(1..7).each { |c| grid.grid_columnconfigure(c, uniform: 'day', minsize: 22) }
|
|
249
|
+
|
|
250
|
+
outer
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def render_notable(parent, year, fonts)
|
|
254
|
+
ft_note, ft_note_b, ft_note_d = fonts
|
|
255
|
+
t = theme
|
|
256
|
+
dates = notable_dates(year)
|
|
257
|
+
|
|
258
|
+
# Split into 3 roughly-equal columns
|
|
259
|
+
third = (dates.size / 3.0).ceil
|
|
260
|
+
cols = dates.each_slice(third).to_a
|
|
261
|
+
|
|
262
|
+
outer = TkFrame.new(parent) {
|
|
263
|
+
borderwidth 1; relief 'solid'; background t[:bg]
|
|
264
|
+
highlightbackground t[:border]; highlightthickness 0
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
inner = TkFrame.new(outer) { background t[:bg] }
|
|
268
|
+
inner.pack(padx: 5, pady: 5, fill: 'both')
|
|
269
|
+
|
|
270
|
+
cols.each_with_index do |entries, ci|
|
|
271
|
+
tw = TkText.new(inner) {
|
|
272
|
+
width 1; height(entries.size + 4)
|
|
273
|
+
font ft_note; borderwidth 0; highlightthickness 0
|
|
274
|
+
wrap 'word'; background t[:bg]; padx 3; pady 2
|
|
275
|
+
cursor ''; insertwidth 0
|
|
276
|
+
tabs '65'
|
|
277
|
+
}
|
|
278
|
+
tw.tag_configure('dt', foreground: t[:fg], font: ft_note_b)
|
|
279
|
+
tw.tag_configure('dtr', foreground: t[:red], font: ft_note_b)
|
|
280
|
+
tw.tag_configure('desc', foreground: t[:desc_fg], font: ft_note_d)
|
|
281
|
+
|
|
282
|
+
entries.each_with_index do |(date, desc, is_red), i|
|
|
283
|
+
dtag = is_red ? 'dtr' : 'dt'
|
|
284
|
+
date_str = "#{date.day}. #{MABBR_NO[date.month - 1]}"
|
|
285
|
+
tw.insert('end', date_str, dtag)
|
|
286
|
+
tw.insert('end', "\t")
|
|
287
|
+
tw.insert('end', desc, 'desc')
|
|
288
|
+
tw.insert('end', "\n") unless i == entries.size - 1
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
tw.state 'disabled'
|
|
292
|
+
tw.grid(row: 0, column: ci * 2, sticky: 'nsew', padx: [0, 2])
|
|
293
|
+
inner.grid_columnconfigure(ci * 2, weight: 1, uniform: 'notecol')
|
|
294
|
+
inner.grid_rowconfigure(0, weight: 1)
|
|
295
|
+
|
|
296
|
+
# vertical separator between columns (except after last)
|
|
297
|
+
if ci < cols.size - 1
|
|
298
|
+
sep = TkFrame.new(inner) { background t[:sep]; width 1 }
|
|
299
|
+
sep.grid(row: 0, column: ci * 2 + 1, sticky: 'ns', padx: 4)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
outer
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def build_calendar(parent, year, fonts)
|
|
307
|
+
ft_mheader, ft_cal, ft_cal_bold, ft_note, ft_note_b, ft_note_d = fonts
|
|
308
|
+
t = theme
|
|
309
|
+
holidays = red_days(year)
|
|
310
|
+
|
|
311
|
+
frame = TkFrame.new(parent) { background t[:bg] }
|
|
312
|
+
|
|
313
|
+
# Single grid for months and notable dates
|
|
314
|
+
grid_f = TkFrame.new(frame) { background t[:bg] }
|
|
315
|
+
grid_f.pack(padx: 10, pady: [2, 8])
|
|
316
|
+
|
|
317
|
+
12.times do |idx|
|
|
318
|
+
m_frame = render_month(grid_f, year, idx + 1, holidays,
|
|
319
|
+
[ft_mheader, ft_cal, ft_cal_bold])
|
|
320
|
+
m_frame.grid(row: idx / 3, column: idx % 3, padx: 3, pady: 3, sticky: 'nsew')
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Notable dates in row 4, spanning all 3 month columns
|
|
324
|
+
note_f = render_notable(grid_f, year, [ft_note, ft_note_b, ft_note_d])
|
|
325
|
+
note_f.grid(row: 4, column: 0, columnspan: 3, padx: 3, pady: [4, 0], sticky: 'ew')
|
|
326
|
+
|
|
327
|
+
# Toggle button below notable dates
|
|
328
|
+
TkButton.new(grid_f) {
|
|
329
|
+
text t[:toggle_label]; font fonts[5] # ft_note_d
|
|
330
|
+
relief 'flat'; borderwidth 0
|
|
331
|
+
foreground t[:dkgray]; background t[:bg]
|
|
332
|
+
activebackground t[:bg]; activeforeground t[:fg]
|
|
333
|
+
command { $dark = !$dark; $rebuild&.call }
|
|
334
|
+
}.grid(row: 5, column: 0, columnspan: 3, pady: [4, 0])
|
|
335
|
+
|
|
336
|
+
3.times { |c| grid_f.grid_columnconfigure(c, weight: 1) }
|
|
337
|
+
|
|
338
|
+
frame
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# -- Title ---------------------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
$top = TkFrame.new(root) { background theme[:bg] }
|
|
344
|
+
$top.pack(fill: 'x', pady: [8, 2])
|
|
345
|
+
|
|
346
|
+
$title_lbl = TkLabel.new($top) {
|
|
347
|
+
text $year.to_s; font ft_title; foreground theme[:title_fg]; background theme[:bg]
|
|
348
|
+
}
|
|
349
|
+
$title_lbl.pack
|
|
350
|
+
|
|
351
|
+
# -- Build & rebuild -----------------------------------------------------------
|
|
352
|
+
|
|
353
|
+
all_fonts = [ft_mheader, ft_cal, ft_cal_bold, ft_note, ft_note_b, ft_note_d]
|
|
354
|
+
$content = nil
|
|
355
|
+
$rebuild = nil
|
|
356
|
+
|
|
357
|
+
$rebuild = proc {
|
|
358
|
+
t = theme
|
|
359
|
+
$content&.destroy
|
|
360
|
+
root.configure(background: t[:bg])
|
|
361
|
+
$top.configure(background: t[:bg])
|
|
362
|
+
$title_lbl.configure(foreground: t[:title_fg], background: t[:bg])
|
|
363
|
+
root.title = "norcal - #{$year}"
|
|
364
|
+
$content = build_calendar(root, $year, all_fonts)
|
|
365
|
+
$content.pack(fill: 'both', expand: true)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
$rebuild.call
|
|
369
|
+
Tk.mainloop
|
metadata
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: norcal
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- baosen
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: tk
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.6'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.6'
|
|
26
|
+
description: A local Norwegian desktop calendar with Tk GUI
|
|
27
|
+
email: chibaosen@gmail.com
|
|
28
|
+
executables:
|
|
29
|
+
- norcal
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- LICENSE
|
|
34
|
+
- README.md
|
|
35
|
+
- bin/norcal
|
|
36
|
+
homepage: https://github.com/baosen/norcal
|
|
37
|
+
licenses:
|
|
38
|
+
- ISC
|
|
39
|
+
metadata: {}
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.0'
|
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
requirements: []
|
|
54
|
+
rubygems_version: 4.0.3
|
|
55
|
+
specification_version: 4
|
|
56
|
+
summary: Norwegian desktop calendar
|
|
57
|
+
test_files: []
|