Changes
3 changed files (+167/-0)
-
-
@@ -0,0 +1,35 @@// 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 CalendarCell : Gtk.Box { public DateTime date { get; construct; } public bool dimmed { get; set; default = false; } private Gtk.Label date_label = new Gtk.Label(""); public CalendarCell(DateTime date) { Object(date: date, orientation: Gtk.Orientation.VERTICAL, spacing: 2); } construct { this.notify["dimmed"].connect(() => { if (this.dimmed) { date_label.css_classes = { "dimmed" }; } else { date_label.css_classes = {}; } }); date_label.label = @"$(date.get_day_of_month())"; this.append(date_label); } } }
-
-
-
@@ -0,0 +1,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); } } }
-
-
-
@@ -38,6 +38,13 @@ "History","view-list-bullet-symbolic" ); stack.add_titled_with_icon( new CalendarView(model), "calendar", "Calendar", "x-office-calendar-symbolic" ); var switcher = new Adw.ViewSwitcher(); switcher.stack = stack;
-