-
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
// Copyright 2025 Shota FUJI
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
using GLib;
namespace TimeTracker.Widget {
private class CalendarView : Gtk.Box {
public Model.Model model { get; construct; }
public DateTime month { get; set construct; }
private Gtk.Grid grid = new Gtk.Grid();
private Gtk.Label title = new Gtk.Label("");
private Array<CalendarCell> cells = new Array<CalendarCell>();
public CalendarView(Model.Model model) {
Object(
model: model,
month: new DateTime.now_local(),
orientation: Gtk.Orientation.VERTICAL,
spacing: 16
);
}
construct {
var header = new Gtk.Box(HORIZONTAL, 4);
header.hexpand = true;
header.margin_top = 4;
header.margin_bottom = header.margin_top;
header.margin_start = 8;
header.margin_end = header.margin_start;
var prev = new Gtk.Button();
prev.label = "Prev";
prev.clicked.connect(() => {
month = month.add_months(-1);
});
header.append(prev);
title.css_classes = { "numeric" };
title.hexpand = true;
title.hexpand_set = true;
header.append(title);
var next = new Gtk.Button();
next.label = "Next";
next.clicked.connect(() => {
month = month.add_months(1);
});
header.append(next);
this.append(header);
this.append(grid);
this.notify["month"].connect(() => {
this.render();
});
this.render();
}
private void render() {
// Ideally, this should be binding but GObject binding not working for
// get-only property... what a half ass system.
title.label = month.format("%Y-%m");
foreach (var cell in cells.data) {
grid.remove(cell);
}
cells = new Array<CalendarCell>();
var first_cell = start_of_week(start_of_month(month));
var last_cell = end_of_week(end_of_month(month));
for (var d = first_cell; d.compare(last_cell) < 1; d = d.add_days(1)) {
var cell = new CalendarCell(d) {
dimmed = d.get_month() != month.get_month(),
};
cell.hexpand = true;
cell.vexpand = true;
cells.append_val(cell);
grid.attach(
cell,
d.get_day_of_week() - 1,
(int) (d.difference(first_cell) / GLib.TimeSpan.DAY / 7),
1, 1
);
}
}
private static DateTime start_of_month(DateTime d) {
return new DateTime(
d.get_timezone(),
d.get_year(), d.get_month(), 1,
0, 0, 0
);
}
private static DateTime end_of_month(DateTime d) {
return start_of_month(d).add_months(1).add_seconds(-1);
}
private static DateTime start_of_day(DateTime d) {
return new DateTime(
d.get_timezone(),
d.get_year(), d.get_month(), d.get_day_of_month(),
0, 0, 0
);
}
private static DateTime start_of_week(DateTime d) {
var day_of_week = d.get_day_of_week();
return start_of_day(d).add_days(-(day_of_week - 1));
}
private static DateTime end_of_week(DateTime d) {
var day_of_week = d.get_day_of_week();
return start_of_day(d).add_days(7 - day_of_week);
}
}
}