Changes
2 changed files (+151/-1)
-
-
@@ -0,0 +1,112 @@// 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 sealed class HistoryRow : Gtk.Box { /** * User requested to start the timer again. */ public signal void start(Model.Timer timer); /** * User requested to delete this entry. */ public signal void remove(Model.Timer timer); public Model.Timer? timer { get; set; } private Gtk.Label title = new Gtk.Label("") { ellipsize = END, halign = START, css_classes = { "heading" }, }; private TimeSpan duration = new TimeSpan() { halign = START, valign = BASELINE, css_classes = { "caption" }, }; private Gtk.Label span = new Gtk.Label("") { halign = START, }; private Adw.SplitButton actions = new Adw.SplitButton() { valign = START, label = "Start", }; construct { var action_group = new SimpleActionGroup(); var delete_action = new SimpleAction("Delete", null); delete_action.activate.connect(() => { if (timer != null) { remove(timer); } }); action_group.add_action(delete_action); this.insert_action_group("HistoryRow", action_group); this.notify["timer"].connect(() => { if (timer == null) { return; } if (timer.title.length > 0) { title.label = timer.title; title.tooltip_text = timer.title; title.remove_css_class("dimmed"); } else { title.label = "(No title)"; title.add_css_class("dimmed"); } var start = new DateTime.from_unix_local(timer.started_at); var end = new DateTime.from_unix_local(timer.stopped_at); duration.span = end.difference(start); var is_span_days = start.get_year() != end.get_year() || start.get_day_of_year() != end.get_day_of_year(); span.label = "%s - %s".printf( start.format("%Y-%m-%d %H:%M"), is_span_days ? end.format("%Y-%m-%d %H:%M") : end.format("%H:%M") ); }); this.orientation = HORIZONTAL; this.spacing = 16; var info = new Gtk.Box(VERTICAL, 6) { hexpand = true, hexpand_set = true, }; var first_row = new Gtk.Box(HORIZONTAL, 8); first_row.append(title); first_row.append(duration); info.append(first_row); info.append(span); this.append(info); var menu = new Menu(); menu.append("Delete", "HistoryRow.Delete"); actions.menu_model = menu; this.append(actions); actions.clicked.connect(() => { if (timer != null) { start(timer); } }); } } }
-
-
-
@@ -12,6 +12,10 @@ namespace TimeTracker.Widget {private class HistoryView : Gtk.Box { public Model.Model model { get; construct; } private Gtk.ListView list = new Gtk.ListView(null, null) { hexpand = true, }; public HistoryView(Model.Model model) { Object(model: model, orientation: Gtk.Orientation.VERTICAL, spacing: 4); }
-
@@ -22,8 +26,42 @@ vexpand = true,}; this.append(scrollable); var list = new TimerList(model.stopped_timers); var factory = new Gtk.SignalListItemFactory(); factory.setup.connect(setup); factory.bind.connect(bind); list.factory = factory; list.model = new Gtk.NoSelection(model.stopped_timers); scrollable.child = list; } private void setup(Gtk.ListItemFactory _factory, Object object) { var list_item = (Gtk.ListItem) object; list_item.selectable = false; list_item.activatable = false; var row = new HistoryRow() { margin_top = 16, margin_bottom = 16, margin_start = 12, margin_end = 12, }; row.start.connect((timer) => { model.update(new Event.TimerStarted(timer.title)); }); row.remove.connect((timer) => { model.update(new Event.TimerDeleted(timer.id)); }); list_item.child = row; } private void bind(Gtk.ListItemFactory _factory, Object object) { var list_item = (Gtk.ListItem) object; var timer = (Model.Timer) list_item.item; var row = (HistoryRow) list_item.child; row.timer = timer; } } }
-