From d065d4b9e8414980df7fbcf3c33e72b748f2dc94 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 23 Apr 2026 16:00:39 -0300 Subject: [PATCH] views/agenda: Port to GListModel This is a major rework of the Agenda view. It splits the view into GcalAgendaViewDay, which represent a single day with events; and GcalAgendaViewDayRow, which is the actual widget with a title label, and a listbox with event widgets. The Agenda view itself merely manages a list of GcalAgendaViewDays, and updates them with the events model, and active dates. This allows us to get rid of the custom listbox row, and the confusing checks for event widgets, and all the bugs the came with it. It also allows for removing the internal GcalRangeTree, and delegate all event management to list models. Part-of: --- po/POTFILES.in | 4 +- src/gui/views/gcal-agenda-view-day-row.blp | 35 ++ src/gui/views/gcal-agenda-view-day-row.c | 297 ++++++++++++ src/gui/views/gcal-agenda-view-day-row.h | 37 ++ src/gui/views/gcal-agenda-view-day.c | 294 ++++++++++++ src/gui/views/gcal-agenda-view-day.h | 39 ++ src/gui/views/gcal-agenda-view.blp | 43 +- src/gui/views/gcal-agenda-view.c | 509 ++++----------------- src/gui/views/meson.build | 3 + src/gui/views/views.gresource.xml | 1 + src/theme/style.css | 22 +- 11 files changed, 832 insertions(+), 452 deletions(-) create mode 100644 src/gui/views/gcal-agenda-view-day-row.blp create mode 100644 src/gui/views/gcal-agenda-view-day-row.c create mode 100644 src/gui/views/gcal-agenda-view-day-row.h create mode 100644 src/gui/views/gcal-agenda-view-day.c create mode 100644 src/gui/views/gcal-agenda-view-day.h diff --git a/po/POTFILES.in b/po/POTFILES.in index c8264472..cdd6711a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -62,8 +62,8 @@ src/gui/importer/gcal-import-dialog.c src/gui/importer/gcal-import-file-row.c src/gui/importer/gcal-importer.c src/gui/shortcuts-dialog.blp -src/gui/views/gcal-agenda-view.blp -src/gui/views/gcal-agenda-view.c +src/gui/views/gcal-agenda-view-day-row.blp +src/gui/views/gcal-agenda-view-day-row.c src/gui/views/gcal-month-popover.blp src/gui/views/gcal-week-grid.c src/gui/views/gcal-week-header.c diff --git a/src/gui/views/gcal-agenda-view-day-row.blp b/src/gui/views/gcal-agenda-view-day-row.blp new file mode 100644 index 00000000..c57a4ec1 --- /dev/null +++ b/src/gui/views/gcal-agenda-view-day-row.blp @@ -0,0 +1,35 @@ +using Gtk 4.0; +using Adw 1; + +template $GcalAgendaViewDayRow: Adw.Bin { + Box { + orientation: vertical; + + Label title_label { + xalign: 0.0; + label: bind $datetime_to_string_func(template.day as <$GcalAgendaViewDay>.date); + + styles [ + "caption-heading", + ] + } + + ListBox listbox { + hexpand: true; + vexpand: true; + selection-mode: none; + row-activated => $on_list_box_row_activated_cb(template) not-swapped; + + [placeholder] + Label { + label: _("No events"); + xalign: 0; + + styles [ + "no-events", + "dimmed", + ] + } + } + } +} diff --git a/src/gui/views/gcal-agenda-view-day-row.c b/src/gui/views/gcal-agenda-view-day-row.c new file mode 100644 index 00000000..5242d9c8 --- /dev/null +++ b/src/gui/views/gcal-agenda-view-day-row.c @@ -0,0 +1,297 @@ +/* + * gcal-agenda-view-day-row.c + * + * Copyright 2026 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "gcal-agenda-view-day-row.h" +#include "gcal-date-time-utils.h" +#include "gcal-debug.h" +#include "gcal-enums.h" +#include "gcal-event-widget.h" + +#include + +struct _GcalAgendaViewDayRow +{ + AdwBin parent_instance; + + GtkListBox *listbox; + GtkLabel *title_label; + + GcalAgendaViewDay *day; +}; + +G_DEFINE_FINAL_TYPE (GcalAgendaViewDayRow, gcal_agenda_view_day_row, ADW_TYPE_BIN) + +enum { + PROP_0, + PROP_DAY, + N_PROPS, +}; + +enum +{ + EVENT_ACTIVATED, + N_SIGNALS, +}; + +static GParamSpec *properties [N_PROPS]; +static guint signals[N_SIGNALS] = { 0, }; + + +/* + * Auxiliary methods + */ + +static gchar * +new_date_header_string (GDateTime *date) +{ + g_autoptr (GDateTime) today = NULL; + g_autoptr (GDateTime) tomorrow = NULL; + g_autoptr (GDateTime) yesterday = NULL; + + if (date == NULL) + return NULL; + + today = g_date_time_new_now_local (); + tomorrow = g_date_time_add_days (today, 1); + yesterday = g_date_time_add_days (today, -1); + + if (gcal_date_time_compare_date (date, today) == 0) + return g_strdup (_("Today")); + else if (gcal_date_time_compare_date (date, tomorrow) == 0) + return g_strdup (_("Tomorrow")); + else if (gcal_date_time_compare_date (date, yesterday) == 0) + return g_strdup (_("Yesterday")); + else + /* + * Translators: %A is the full day name, %B is the month name + * and %d is the day of the month as a number between 0 and 31. + * More formats can be found on the doc: + * https://docs.gtk.org/glib/method.DateTime.format.html + */ + return g_date_time_format (date, _("%A %B %d")); +} + + +/* + * Callbacks + */ + +static void +on_event_widget_activated_cb (GcalEventWidget *event_widget, + GcalAgendaViewDayRow *self) +{ + g_assert (GCAL_IS_AGENDA_VIEW_DAY_ROW (self)); + g_assert (GCAL_IS_EVENT_WIDGET (event_widget)); + + g_signal_emit (self, signals[EVENT_ACTIVATED], 0, event_widget); +} + +static void +on_list_box_row_activated_cb (GtkListBox *listbox, + GtkListBoxRow *row, + GcalAgendaViewDayRow *self) +{ + GtkWidget *child = gtk_list_box_row_get_child (row); + + g_assert (GCAL_IS_AGENDA_VIEW_DAY_ROW (self)); + g_assert (GCAL_IS_EVENT_WIDGET (child)); + + g_signal_emit (self, signals[EVENT_ACTIVATED], 0, child); +} + +static GtkWidget * +create_day_row_func (gpointer item, + gpointer user_data) +{ + GcalAgendaViewDayRow *self; + GcalTimestampPolicy timestamp_policy; + GcalEvent *event; + GtkWidget *widget; + GtkWidget *row; + + self = (GcalAgendaViewDayRow *) user_data; + + g_assert (GCAL_IS_EVENT (item)); + g_assert (GCAL_IS_AGENDA_VIEW_DAY_ROW (self)); + + event = item; + + /* Create and add the new event widget */ + if (gcal_event_get_all_day (event) || gcal_event_is_multiday (event)) + timestamp_policy = GCAL_TIMESTAMP_POLICY_END; + else + timestamp_policy = GCAL_TIMESTAMP_POLICY_START; + + widget = g_object_new (GCAL_TYPE_EVENT_WIDGET, + "event", event, + "orientation", GTK_ORIENTATION_VERTICAL, + "timestamp-policy", timestamp_policy, + "focusable", FALSE, + NULL); + + row = g_object_new (GTK_TYPE_LIST_BOX_ROW, + "child", widget, + "activatable", TRUE, + NULL); + + gtk_accessible_update_property (GTK_ACCESSIBLE (row), + GTK_ACCESSIBLE_PROPERTY_HAS_POPUP, TRUE, + -1); + + gtk_accessible_update_relation (GTK_ACCESSIBLE (row), + GTK_ACCESSIBLE_RELATION_LABELLED_BY, widget, NULL, + -1); + + g_signal_connect (widget, "activate", G_CALLBACK (on_event_widget_activated_cb), self); + + return row; +} + +static char * +datetime_to_string_func (gpointer item, + GDateTime *datetime, + gpointer user_data) +{ + return datetime ? new_date_header_string (datetime) : g_strdup (""); +} + + +/* + * GObject overrides + */ + +static void +gcal_agenda_view_day_row_dispose (GObject *object) +{ + GcalAgendaViewDayRow *self = (GcalAgendaViewDayRow *)object; + + g_clear_object (&self->day); + + gtk_widget_dispose_template (GTK_WIDGET (self), GCAL_TYPE_AGENDA_VIEW_DAY_ROW); + + G_OBJECT_CLASS (gcal_agenda_view_day_row_parent_class)->dispose (object); +} + +static void +gcal_agenda_view_day_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalAgendaViewDayRow *self = GCAL_AGENDA_VIEW_DAY_ROW (object); + + switch (prop_id) + { + case PROP_DAY: + g_value_set_object (value, self->day); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_agenda_view_day_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalAgendaViewDayRow *self = GCAL_AGENDA_VIEW_DAY_ROW (object); + + switch (prop_id) + { + case PROP_DAY: + gcal_agenda_view_day_row_set_day (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_agenda_view_day_row_class_init (GcalAgendaViewDayRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gcal_agenda_view_day_row_dispose; + object_class->get_property = gcal_agenda_view_day_row_get_property; + object_class->set_property = gcal_agenda_view_day_row_set_property; + + properties[PROP_DAY] = g_param_spec_object ("day", NULL, NULL, + GCAL_TYPE_AGENDA_VIEW_DAY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + signals[EVENT_ACTIVATED] = g_signal_new ("event-activated", + GCAL_TYPE_AGENDA_VIEW_DAY_ROW, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 1, + GCAL_TYPE_EVENT_WIDGET); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/views/gcal-agenda-view-day-row.ui"); + + gtk_widget_class_bind_template_child (widget_class, GcalAgendaViewDayRow, listbox); + gtk_widget_class_bind_template_child (widget_class, GcalAgendaViewDayRow, title_label); + + gtk_widget_class_bind_template_callback (widget_class, datetime_to_string_func); + gtk_widget_class_bind_template_callback (widget_class, on_list_box_row_activated_cb); + + gtk_widget_class_set_css_name (widget_class, "agendaviewdayrow"); +} + +static void +gcal_agenda_view_day_row_init (GcalAgendaViewDayRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GcalAgendaViewDay * +gcal_agenda_view_day_row_get_day (GcalAgendaViewDayRow *self) +{ + g_assert (GCAL_IS_AGENDA_VIEW_DAY_ROW (self)); + + return self->day; +} + +void +gcal_agenda_view_day_row_set_day (GcalAgendaViewDayRow *self, + GcalAgendaViewDay *day) +{ + g_assert (GCAL_IS_AGENDA_VIEW_DAY_ROW (self)); + g_assert (day == NULL || GCAL_IS_AGENDA_VIEW_DAY (day)); + + if (g_set_object (&self->day, day)) + { + gtk_list_box_bind_model (GTK_LIST_BOX (self->listbox), + day ? G_LIST_MODEL (day) : NULL, + day ? create_day_row_func : NULL, + day ? self : NULL, + NULL); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DAY]); + } +} diff --git a/src/gui/views/gcal-agenda-view-day-row.h b/src/gui/views/gcal-agenda-view-day-row.h new file mode 100644 index 00000000..9f9007da --- /dev/null +++ b/src/gui/views/gcal-agenda-view-day-row.h @@ -0,0 +1,37 @@ +/* + * gcal-agenda-view-day-row.h + * + * Copyright 2026 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "gcal-agenda-view-day.h" + +G_BEGIN_DECLS + +#define GCAL_TYPE_AGENDA_VIEW_DAY_ROW (gcal_agenda_view_day_row_get_type()) +G_DECLARE_FINAL_TYPE (GcalAgendaViewDayRow, gcal_agenda_view_day_row, GCAL, AGENDA_VIEW_DAY_ROW, AdwBin) + +GcalAgendaViewDay *gcal_agenda_view_day_row_get_day (GcalAgendaViewDayRow *self); +void gcal_agenda_view_day_row_set_day (GcalAgendaViewDayRow *self, + GcalAgendaViewDay *day); + +G_END_DECLS diff --git a/src/gui/views/gcal-agenda-view-day.c b/src/gui/views/gcal-agenda-view-day.c new file mode 100644 index 00000000..33d34c49 --- /dev/null +++ b/src/gui/views/gcal-agenda-view-day.c @@ -0,0 +1,294 @@ +/* + * gcal-agenda-view-day.c + * + * Copyright 2026 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "GcalAgendaViewDay" + +#include "gcal-agenda-view-day.h" + +#include "gcal-date-time-utils.h" +#include "gcal-range.h" + +struct _GcalAgendaViewDay +{ + GObject parent_instance; + + GDateTime *date; + GcalRange *range; + + GtkFilterListModel *filtered_events; +}; + +static void g_list_model_interface_init (GListModelInterface *iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE (GcalAgendaViewDay, gcal_agenda_view_day, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_interface_init)) + +enum { + PROP_0, + PROP_DATE, + PROP_MODEL, + PROP_N_ITEMS, + N_PROPS, +}; + +static GParamSpec *properties [N_PROPS]; + + +/* + * Callbacks + */ +static void +on_filtered_events_items_changed_cb (GListModel *model, + unsigned int position, + unsigned int removed, + unsigned int added, + GcalAgendaViewDay *self) +{ + g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added); + if (removed != added) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_ITEMS]); +} + + +/* + * GListModel interface + */ + +static GType +gcal_agenda_view_day_get_item_type (GListModel *model G_GNUC_UNUSED) +{ + return GCAL_TYPE_EVENT; +} + +static unsigned int +gcal_agenda_view_day_get_n_items (GListModel *model) +{ + GcalAgendaViewDay *self = (GcalAgendaViewDay *) model; + + g_assert (GCAL_IS_AGENDA_VIEW_DAY (self)); + + return g_list_model_get_n_items (G_LIST_MODEL (self->filtered_events)); +} + +static gpointer +gcal_agenda_view_day_get_item (GListModel *model, + guint position) +{ + GcalAgendaViewDay *self = (GcalAgendaViewDay *) model; + + g_assert (GCAL_IS_AGENDA_VIEW_DAY (self)); + + return g_list_model_get_item (G_LIST_MODEL (self->filtered_events), position); +} + +static void +g_list_model_interface_init (GListModelInterface *iface) +{ + iface->get_item_type = gcal_agenda_view_day_get_item_type; + iface->get_n_items = gcal_agenda_view_day_get_n_items; + iface->get_item = gcal_agenda_view_day_get_item; +} + + +/* + * GObject overrides + */ + +static void +gcal_agenda_view_day_dispose (GObject *object) +{ + GcalAgendaViewDay *self = (GcalAgendaViewDay *)object; + + g_clear_pointer (&self->range, gcal_range_unref); + gcal_clear_date_time (&self->date); + + G_OBJECT_CLASS (gcal_agenda_view_day_parent_class)->dispose (object); +} + +static void +gcal_agenda_view_day_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalAgendaViewDay *self = GCAL_AGENDA_VIEW_DAY (object); + + switch (prop_id) + { + case PROP_DATE: + g_value_set_boxed (value, self->date); + break; + + case PROP_MODEL: + g_value_set_object (value, gcal_agenda_view_day_get_model (self)); + break; + + case PROP_N_ITEMS: + g_value_set_uint (value, g_list_model_get_n_items (G_LIST_MODEL (self->filtered_events))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_agenda_view_day_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalAgendaViewDay *self = GCAL_AGENDA_VIEW_DAY (object); + + switch (prop_id) + { + case PROP_DATE: + gcal_agenda_view_day_set_date (self, g_value_get_boxed (value)); + break; + + case PROP_MODEL: + gcal_agenda_view_day_set_model (self, g_value_get_object (value)); + break; + + case PROP_N_ITEMS: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_agenda_view_day_class_init (GcalAgendaViewDayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gcal_agenda_view_day_dispose; + object_class->get_property = gcal_agenda_view_day_get_property; + object_class->set_property = gcal_agenda_view_day_set_property; + + properties[PROP_DATE] = g_param_spec_boxed ("date", NULL, NULL, + G_TYPE_DATE_TIME, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + properties[PROP_MODEL] = g_param_spec_object ("model", NULL, NULL, + G_TYPE_LIST_MODEL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + properties[PROP_N_ITEMS] = g_param_spec_uint ("n-items", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static gboolean +filter_events_func (gpointer item, + gpointer user_data) +{ + GcalAgendaViewDay *self = (GcalAgendaViewDay *) user_data; + + g_assert (GCAL_IS_EVENT (item)); + g_assert (GCAL_IS_AGENDA_VIEW_DAY (self)); + + if (!self->range) + return FALSE; + + return gcal_event_overlaps (item, self->range); +} + +static void +gcal_agenda_view_day_init (GcalAgendaViewDay *self) +{ + self->filtered_events = gtk_filter_list_model_new (NULL, + GTK_FILTER (gtk_custom_filter_new (filter_events_func, self, NULL))); + g_signal_connect (self->filtered_events, "items-changed", G_CALLBACK (on_filtered_events_items_changed_cb), self); +} + +GcalAgendaViewDay * +gcal_agenda_view_day_new (void) +{ + return g_object_new (GCAL_TYPE_AGENDA_VIEW_DAY, NULL); +} + +GDateTime * +gcal_agenda_view_day_get_date (GcalAgendaViewDay *self) +{ + g_assert (GCAL_IS_AGENDA_VIEW_DAY (self)); + + return self->date; +} + +void +gcal_agenda_view_day_set_date (GcalAgendaViewDay *self, + GDateTime *date) +{ + g_assert (GCAL_IS_AGENDA_VIEW_DAY (self)); + + if (gcal_set_date_time (&self->date, date)) + { + GtkFilter *filter = NULL; + + g_clear_pointer (&self->range, gcal_range_unref); + + if (date) + { + g_autoptr (GDateTime) start = NULL; + g_autoptr (GDateTime) end = NULL; + + start = g_date_time_new (g_date_time_get_timezone (date), + g_date_time_get_year (date), + g_date_time_get_month (date), + g_date_time_get_day_of_month (date), + 0, 0, 0); + end = g_date_time_add_days (start, 1); + + self->range = gcal_range_new_take (g_steal_pointer (&start), + g_steal_pointer (&end), + GCAL_RANGE_DEFAULT); + } + + filter = gtk_filter_list_model_get_filter (self->filtered_events); + // FIXME: better filter change + gtk_filter_changed (filter, GTK_FILTER_CHANGE_DIFFERENT); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DATE]); + } +} + +GListModel * +gcal_agenda_view_day_get_model (GcalAgendaViewDay *self) +{ + g_assert (GCAL_IS_AGENDA_VIEW_DAY (self)); + + return gtk_filter_list_model_get_model (self->filtered_events); +} + +void +gcal_agenda_view_day_set_model (GcalAgendaViewDay *self, + GListModel *model) +{ + g_assert (GCAL_IS_AGENDA_VIEW_DAY (self)); + + if (gtk_filter_list_model_get_model (self->filtered_events) != model) + { + gtk_filter_list_model_set_model (self->filtered_events, model); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); + } +} diff --git a/src/gui/views/gcal-agenda-view-day.h b/src/gui/views/gcal-agenda-view-day.h new file mode 100644 index 00000000..4fe78e32 --- /dev/null +++ b/src/gui/views/gcal-agenda-view-day.h @@ -0,0 +1,39 @@ +/* + * gcal-agenda-view-day.h + * + * Copyright 2026 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "gcal-event.h" + +G_BEGIN_DECLS + +#define GCAL_TYPE_AGENDA_VIEW_DAY (gcal_agenda_view_day_get_type()) +G_DECLARE_FINAL_TYPE (GcalAgendaViewDay, gcal_agenda_view_day, GCAL, AGENDA_VIEW_DAY, GObject) + +GcalAgendaViewDay *gcal_agenda_view_day_new (void); +GDateTime *gcal_agenda_view_day_get_date (GcalAgendaViewDay *self); +void gcal_agenda_view_day_set_date (GcalAgendaViewDay *self, + GDateTime *date); +GListModel *gcal_agenda_view_day_get_model (GcalAgendaViewDay *self); +void gcal_agenda_view_day_set_model (GcalAgendaViewDay *self, + GListModel *model); + +G_END_DECLS diff --git a/src/gui/views/gcal-agenda-view.blp b/src/gui/views/gcal-agenda-view.blp index 037eb25f..3bfbc106 100644 --- a/src/gui/views/gcal-agenda-view.blp +++ b/src/gui/views/gcal-agenda-view.blp @@ -13,26 +13,37 @@ template $GcalAgendaView: Box { ScrolledWindow scrolled_window { hexpand: true; vexpand: true; + hscrollbar-policy: never; - ListBox list_box { - hexpand: true; - selection-mode: none; - vexpand: true; - row-activated => $on_list_box_row_activated_cb(template) not-swapped; + ListView list_view { + hscroll-policy: natural; + vscroll-policy: natural; + single-click-activate: true; - ListBoxRow no_events_row { - activatable: false; + model: Gtk.NoSelection { + model: FilterListModel filtered_days { + watch-items: true; - child: Label { - label: _("No events"); - xalign: 0; - - styles [ - "no-events", - "dimmed", - ] + filter: BoolFilter { + expression: expr $n_items_and_date_to_boolean( + item as <$GcalAgendaViewDay>.n-items, + item as <$GcalAgendaViewDay>.date, + ) as ; + }; }; - } + }; + + factory: BuilderListItemFactory { + template ListItem { + activatable: false; + selectable: false; + + child: $GcalAgendaViewDayRow { + day: bind template.item; + event-activated => $on_day_row_event_activated_cb(template) not-swapped; + }; + } + }; } } } diff --git a/src/gui/views/gcal-agenda-view.c b/src/gui/views/gcal-agenda-view.c index e21a910c..f1e02bc4 100644 --- a/src/gui/views/gcal-agenda-view.c +++ b/src/gui/views/gcal-agenda-view.c @@ -19,6 +19,8 @@ #define G_LOG_DOMAIN "GcalAgendaView" #include "gcal-agenda-view.h" +#include "gcal-agenda-view-day.h" +#include "gcal-agenda-view-day-row.h" #include "gcal-debug.h" #include "gcal-enums.h" #include "gcal-event-widget.h" @@ -28,7 +30,6 @@ #include "gcal-view-private.h" #include -#include #include @@ -43,14 +44,13 @@ struct _GcalAgendaView { GtkBox parent; - GtkWidget *list_box; - GtkWidget *no_events_row; GtkWidget *scrolled_window; /* property */ GDateTime *date; + GListStore *days_model; + GtkFilterListModel *filtered_days; - GcalRangeTree *events; guint scroll_grid_timeout_id; gulong stack_page_changed_id; @@ -82,132 +82,6 @@ G_DEFINE_TYPE_WITH_CODE (GcalAgendaView, gcal_agenda_view, GTK_TYPE_BOX, * Auxiliary methods */ -static ChildData* -child_data_new (GtkWidget *widget, - GcalEvent *event, - GcalAgendaView *self) -{ - ChildData *data; - - data = g_new (ChildData, 1); - data->widget = widget; - data->event = g_object_ref (event); - data->self = self; - - return data; -} - -static void -child_data_free (gpointer data) -{ - ChildData *child_data = data; - - if (!child_data) - return; - - if (child_data->widget && child_data->self) - { - gtk_list_box_remove (GTK_LIST_BOX (child_data->self->list_box), child_data->widget); - child_data->widget = NULL; - child_data->self = NULL; - } - g_clear_object (&child_data->event); - g_free (child_data); -} - -static GDateTime * -get_start_date_for_row (GcalAgendaView *self, - GtkListBoxRow *row) -{ - GtkWidget *widget = gtk_list_box_row_get_child (row); - - if (GCAL_IS_EVENT_WIDGET (widget)) - { - GDateTime *date = gcal_event_widget_get_date_start (GCAL_EVENT_WIDGET (widget)); - - /* Pretend events start at least on the selected date */ - if (gcal_date_time_compare_date (date, self->date) > 0) - return date; - else - return self->date; - } - - - return self->date; -} - -static GDateTime * -get_end_date_for_row (GcalAgendaView *self, - GtkListBoxRow *row) -{ - GtkWidget *widget = gtk_list_box_row_get_child (row); - - if (GCAL_IS_EVENT_WIDGET (widget)) - return gcal_event_widget_get_date_end (GCAL_EVENT_WIDGET (widget)); - - return self->date; -} - -static gboolean -get_row_is_multiday (GtkListBoxRow *row) -{ - GtkWidget *widget = gtk_list_box_row_get_child (row); - - if (GCAL_IS_EVENT_WIDGET (widget)) - { - GcalEvent *event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget)); - - return gcal_event_is_multiday (event); - } - - return FALSE; -} - -static gboolean -get_row_is_all_day (GtkListBoxRow *row) -{ - GtkWidget *widget = gtk_list_box_row_get_child (row); - - if (GCAL_IS_EVENT_WIDGET (widget)) - { - GcalEvent *event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget)); - - return gcal_event_get_all_day (event); - } - - return FALSE; -} - -static gchar * -new_date_header_string (GDateTime *date) -{ - g_autoptr (GDateTime) today = NULL; - g_autoptr (GDateTime) tomorrow = NULL; - g_autoptr (GDateTime) yesterday = NULL; - - if (date == NULL) - return NULL; - - today = g_date_time_new_now_local (); - tomorrow = g_date_time_add_days (today, 1); - yesterday = g_date_time_add_days (today, -1); - - if (gcal_date_time_compare_date (date, today) == 0) - return g_strdup (_("Today")); - else if (gcal_date_time_compare_date (date, tomorrow) == 0) - return g_strdup (_("Tomorrow")); - else if (gcal_date_time_compare_date (date, yesterday) == 0) - return g_strdup (_("Yesterday")); - else - /* - * Translators: %A is the full day name, %B is the month name - * and %d is the day of the month as a number between 0 and 31. - * More formats can be found on the doc: - * https://docs.gtk.org/glib/method.DateTime.format.html - */ - return g_date_time_format (date, _("%A %B %d")); -} - /* * Callbacks @@ -226,20 +100,6 @@ stack_visible_child_changed_cb (AdwViewStack *stack, g_clear_signal_handler (&self->stack_page_changed_id, stack); } -/* Auxiliary methods */ -static void -update_no_events_row (GcalAgendaView *self) -{ - gboolean visible = !self->events_on_date; - gboolean was_visible = gtk_widget_get_visible (self->no_events_row); - - if (visible != was_visible) - { - gtk_widget_set_visible (self->no_events_row, visible); - gtk_list_box_invalidate_headers (GTK_LIST_BOX (self->list_box)); - } -} - static gboolean update_grid_scroll_position (GcalAgendaView *self) { @@ -315,146 +175,30 @@ schedule_position_scroll (GcalAgendaView *self) self); } -static GDateTime* -all_day_to_local (GDateTime *dt) +static gboolean +n_items_and_date_to_boolean (GcalAgendaViewDay *day, + unsigned int n_items, + GDateTime *date, + gpointer user_data) { - return g_date_time_new_local (g_date_time_get_year (dt), - g_date_time_get_month (dt), - g_date_time_get_day_of_month (dt), - 0, 0, 0); -} + GcalAgendaView *self = (GcalAgendaView *) user_data; + g_autoptr (GcalRange) view_range = NULL; -static int -sort_func (GtkListBoxRow *row1, - GtkListBoxRow *row2, - gpointer user_data) -{ - GcalAgendaView *self = GCAL_AGENDA_VIEW (user_data); - g_autoptr (GDateTime) start_date1 = get_start_date_for_row (self, row1); - g_autoptr (GDateTime) start_date2 = get_start_date_for_row (self, row2); - g_autoptr (GDateTime) end_date1 = NULL; - g_autoptr (GDateTime) end_date2 = NULL; - gboolean multiday1, multiday2; - gboolean all_day1, all_day2; - int result; + g_assert (GCAL_IS_AGENDA_VIEW (self)); + g_assert (GCAL_IS_AGENDA_VIEW_DAY (day)); + g_assert (self->date != NULL); - all_day1 = get_row_is_all_day (row1); - all_day2 = get_row_is_all_day (row2); + view_range = gcal_timeline_subscriber_get_range (GCAL_TIMELINE_SUBSCRIBER (self)); + if (!gcal_range_contains_datetime (view_range, date)) + return FALSE; - start_date1 = all_day1 ? all_day_to_local (start_date1) : g_date_time_to_local (start_date1); - start_date2 = all_day2 ? all_day_to_local (start_date2) : g_date_time_to_local (start_date2); + if (n_items > 0) + return TRUE; - result = g_date_time_compare (start_date1, start_date2); + if (gcal_date_time_compare_date (date, self->date) == 0) + return TRUE; - /* Sort multi day events before multi single day events of the same day. */ - if (result != 0) - return result; - - multiday1 = get_row_is_multiday (row1); - multiday2 = get_row_is_multiday (row2); - - if (multiday1 != multiday2) - return multiday1 ? -1 : 1; - - if (all_day1 != all_day2) - return all_day1 ? -1 : 1; - - end_date1 = g_date_time_to_local (get_end_date_for_row (self, row1)); - end_date2 = g_date_time_to_local (get_end_date_for_row (self, row2)); - - return g_date_time_compare (end_date1, end_date2); -} - -static void -update_header (GtkListBoxRow *row, - GtkListBoxRow *before, - gpointer user_data) -{ - GcalAgendaView *self = GCAL_AGENDA_VIEW (user_data); - GtkWidget *widget = gtk_list_box_row_get_child (row); - gboolean event_all_day = get_row_is_all_day (row); - g_autoptr (GDateTime) start = get_start_date_for_row (self, row); - g_autoptr (GDateTime) end = get_end_date_for_row (self, row); - g_autoptr (GDateTime) today = NULL; - g_autoptr (GDateTime) end_midnight = NULL; - GcalEvent *event = NULL; - g_autofree gchar *label = NULL; - GtkWidget *header; - - start = event_all_day ? g_date_time_ref (start) : g_date_time_to_local (start); - end = event_all_day ? g_date_time_ref (end) : g_date_time_to_local (end); - - if (GCAL_IS_EVENT_WIDGET (widget)) - event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget)); - - today = g_date_time_new_now_local (); - - if (before) - { - g_autoptr (GDateTime) before_start = get_start_date_for_row (self, before); - g_autoptr (GDateTime) before_end = get_end_date_for_row (self, before); - gboolean before_all_day = get_row_is_all_day (before); - - before_start = before_all_day ? g_date_time_ref (before_start) : g_date_time_to_local (before_start); - before_end = before_all_day ? g_date_time_ref (before_end) : g_date_time_to_local (before_end); - - if (get_row_is_multiday (row) && - get_row_is_multiday (before) && - gcal_date_time_compare_date (start, before_start) == 0 && - gcal_date_time_compare_date (start, today) == 0) - { - gtk_list_box_row_set_header (row, NULL); - return; - } - - end_midnight = event_all_day ? g_date_time_ref (end) : g_date_time_add_seconds (end, -1); - - if (gcal_date_time_compare_date (start, before_start) == 0 && - gcal_date_time_compare_date (end_midnight, before_end) == 0) - { - gtk_list_box_row_set_header (row, NULL); - return; - } - - /* All day events don't need to check for end date */ - if (event && - (before_all_day || event_all_day) && - !get_row_is_multiday (before) && - gcal_date_time_compare_date (start, before_start) == 0) - { - gtk_list_box_row_set_header (row, NULL); - return; - } - } - - if (get_row_is_multiday (row)) - { - g_autofree gchar *start_label = NULL; - g_autofree gchar *end_label = NULL; - if (gcal_date_time_compare_date (start, today) == 0) - { - label = g_strdup (_("On-going")); - } - else - { - start_label = new_date_header_string (start); - end_label = new_date_header_string (end); - - label = g_strdup_printf ("%s – %s", start_label, end_label); - } - } - else - { - label = new_date_header_string (start); - } - - header = g_object_new (GTK_TYPE_LABEL, - "label", label, - "xalign", 0.0f, - NULL); - gtk_widget_add_css_class (header, "caption-heading"); - - gtk_list_box_row_set_header (row, header); + return FALSE; } @@ -470,23 +214,6 @@ gcal_agenda_view_get_date (GcalView *view) return self->date; } -static inline gboolean -count_events_on_data (GcalRange *range, - gpointer data, - gpointer user_data) -{ - ChildData *child_data = data; - GcalAgendaView *self = user_data; - g_autoptr (GDateTime) event_date = gcal_event_get_date_start (child_data->event); - - event_date = gcal_event_get_all_day (child_data->event) ? g_date_time_ref (event_date) : g_date_time_to_local (event_date); - - if (gcal_date_time_compare_date (self->date, event_date) == 0) - self->events_on_date++; - - return FALSE; -} - static void gcal_agenda_view_set_date (GcalView *view, GDateTime *date) @@ -497,17 +224,21 @@ gcal_agenda_view_set_date (GcalView *view, gcal_set_date_time (&self->date, date); + for (unsigned int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->days_model)); i++) + { + g_autoptr (GcalAgendaViewDay) day = NULL; + g_autoptr (GDateTime) next_date = NULL; + + day = g_list_model_get_item (G_LIST_MODEL (self->days_model), i); + next_date = g_date_time_add_days (date, i); + + gcal_agenda_view_day_set_date (day, next_date); + } + schedule_position_scroll (self); - self->events_on_date = 0; - gcal_range_tree_traverse (self->events, G_IN_ORDER, count_events_on_data, self); - update_no_events_row (self); - gcal_timeline_subscriber_range_changed (GCAL_TIMELINE_SUBSCRIBER (view)); - if (self->events_on_date == 0) - gtk_list_box_invalidate_sort (GTK_LIST_BOX (self->list_box)); - GCAL_EXIT; } @@ -565,24 +296,18 @@ gcal_view_interface_init (GcalViewInterface *iface) */ static void -on_event_widget_activated_cb (GcalEventWidget *event_widget, - GcalAgendaView *self) +on_day_row_event_activated_cb (GcalAgendaViewDayRow *day_row, + GcalEventWidget *event_widget, + gpointer user_data) { + GcalAgendaView *self; + + self = GCAL_AGENDA_VIEW (gtk_widget_get_ancestor (GTK_WIDGET (day_row), GCAL_TYPE_AGENDA_VIEW)); + g_assert (GCAL_IS_AGENDA_VIEW (self)); + gcal_view_event_activated (GCAL_VIEW (self), event_widget); } -static void -on_list_box_row_activated_cb (GtkListBox *list_box, - GtkListBoxRow *row, - GcalAgendaView *self) -{ - GtkWidget *child = gtk_list_box_row_get_child (row); - - g_assert (GCAL_IS_EVENT_WIDGET (child)); - - gcal_view_event_activated (GCAL_VIEW (self), GCAL_EVENT_WIDGET (child)); -} - static GcalRange* gcal_agenda_view_get_range (GcalTimelineSubscriber *subscriber) { @@ -597,107 +322,12 @@ static void gcal_agenda_view_add_event (GcalTimelineSubscriber *subscriber, GcalEvent *event) { - GcalAgendaView *self = GCAL_AGENDA_VIEW (subscriber); - g_autoptr (GDateTime) event_date = gcal_event_get_date_start (event); - GtkWidget *widget, *row; - GcalTimestampPolicy timestamp_policy; - - GCAL_ENTRY; - - /* FIXME Check gcal_event_is_multiday (event) and gcal_event_get_all_day (event) */ - - g_object_ref (event); - - event_date = gcal_event_get_all_day (event) ? g_date_time_ref (event_date) : g_date_time_to_local (event_date); - - if (gcal_date_time_compare_date (self->date, event_date) == 0) - { - self->events_on_date++; - update_no_events_row (self); - } - - /* Create and add the new event widget */ - if (gcal_event_get_all_day (event) || gcal_event_is_multiday (event)) - timestamp_policy = GCAL_TIMESTAMP_POLICY_END; - else - timestamp_policy = GCAL_TIMESTAMP_POLICY_START; - - widget = g_object_new (GCAL_TYPE_EVENT_WIDGET, - "event", event, - "orientation", GTK_ORIENTATION_VERTICAL, - "timestamp-policy", timestamp_policy, - "focusable", FALSE, - NULL); - - row = g_object_new (GTK_TYPE_LIST_BOX_ROW, - "child", widget, - NULL); - - gcal_range_tree_add_range (self->events, - gcal_event_get_range (event), - child_data_new (row, event, self)); - - gtk_accessible_update_property (GTK_ACCESSIBLE (row), - GTK_ACCESSIBLE_PROPERTY_HAS_POPUP, TRUE, - -1); - - gtk_accessible_update_relation (GTK_ACCESSIBLE (row), - GTK_ACCESSIBLE_RELATION_LABELLED_BY, widget, NULL, - -1); - - g_signal_connect (widget, "activate", G_CALLBACK (on_event_widget_activated_cb), self); - - gtk_list_box_append (GTK_LIST_BOX (self->list_box), row); - - gtk_list_box_invalidate_headers (GTK_LIST_BOX (self->list_box)); - - GCAL_EXIT; } static void gcal_agenda_view_remove_event (GcalTimelineSubscriber *subscriber, GcalEvent *event) { - GcalAgendaView *self = GCAL_AGENDA_VIEW (subscriber); - const gchar *uid = gcal_event_get_uid (event); - g_autoptr (GDateTime) event_date = gcal_event_get_date_start (event); - GPtrArray *widgets; - guint i; - - GCAL_ENTRY; - - widgets = gcal_range_tree_get_all_data (self->events); - - event_date = gcal_event_get_all_day (event) ? g_date_time_ref (event_date) : g_date_time_to_local (event_date); - - if (gcal_date_time_compare_date (self->date, event_date) == 0) - { - self->events_on_date--; - update_no_events_row (self); - } - - for (i = 0; widgets && i < widgets->len; i++) - { - ChildData *data; - GtkWidget *widget; - GcalEvent *event; - - data = g_ptr_array_index (widgets, i); - widget = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (data->widget)); - event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget)); - - if (g_strcmp0 (gcal_event_get_uid (event), uid) != 0) - continue; - - gcal_range_tree_remove_range (self->events, gcal_event_get_range (event), data); - gtk_widget_queue_allocate (GTK_WIDGET (self)); - } - - g_clear_pointer (&widgets, g_ptr_array_unref); - - gtk_list_box_invalidate_headers (GTK_LIST_BOX (self->list_box)); - - GCAL_EXIT; } static void @@ -705,10 +335,24 @@ gcal_agenda_view_update_event (GcalTimelineSubscriber *subscriber, GcalEvent *old_event, GcalEvent *event) { +} + +static void +gcal_agenda_view_set_model (GcalTimelineSubscriber *subscriber, + GListModel *model) +{ + GcalAgendaView *self; + GCAL_ENTRY; - gcal_agenda_view_remove_event (subscriber, old_event); - gcal_agenda_view_add_event (subscriber, event); + self = GCAL_AGENDA_VIEW (subscriber); + + for (unsigned int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->days_model)); i++) + { + g_autoptr (GcalAgendaViewDay) day = g_list_model_get_item (G_LIST_MODEL (self->days_model), i); + + gcal_agenda_view_day_set_model (day, model); + } GCAL_EXIT; } @@ -720,6 +364,7 @@ gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface) iface->add_event = gcal_agenda_view_add_event; iface->update_event = gcal_agenda_view_update_event; iface->remove_event = gcal_agenda_view_remove_event; + iface->set_model = gcal_agenda_view_set_model; } @@ -730,22 +375,17 @@ gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface) static void gcal_agenda_view_dispose (GObject *object) { - GcalAgendaView *self; + GcalAgendaView *self = GCAL_AGENDA_VIEW (object); - self = GCAL_AGENDA_VIEW (object); + gtk_widget_dispose_template (GTK_WIDGET (self), GCAL_TYPE_AGENDA_VIEW); - g_clear_pointer (&self->events, gcal_range_tree_unref); - - /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (gcal_agenda_view_parent_class)->dispose (object); } static void gcal_agenda_view_finalize (GObject *object) { - GcalAgendaView *self; - - self = GCAL_AGENDA_VIEW (object); + GcalAgendaView *self = GCAL_AGENDA_VIEW (object); g_clear_pointer (&self->date, g_date_time_unref); @@ -804,6 +444,8 @@ gcal_agenda_view_class_init (GcalAgendaViewClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + g_type_ensure (GCAL_TYPE_AGENDA_VIEW_DAY_ROW); + object_class->dispose = gcal_agenda_view_dispose; object_class->finalize = gcal_agenda_view_finalize; object_class->set_property = gcal_agenda_view_set_property; @@ -814,11 +456,11 @@ gcal_agenda_view_class_init (GcalAgendaViewClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/views/gcal-agenda-view.ui"); - gtk_widget_class_bind_template_child (widget_class, GcalAgendaView, list_box); - gtk_widget_class_bind_template_child (widget_class, GcalAgendaView, no_events_row); + gtk_widget_class_bind_template_child (widget_class, GcalAgendaView, filtered_days); gtk_widget_class_bind_template_child (widget_class, GcalAgendaView, scrolled_window); - gtk_widget_class_bind_template_callback (widget_class, on_list_box_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, n_items_and_date_to_boolean); + gtk_widget_class_bind_template_callback (widget_class, on_day_row_event_activated_cb); gtk_widget_class_set_css_name (widget_class, "agenda-view"); } @@ -828,8 +470,21 @@ gcal_agenda_view_init (GcalAgendaView *self) { gtk_widget_init_template (GTK_WIDGET (self)); - self->events = gcal_range_tree_new_with_free_func (child_data_free); + self->date = g_date_time_new_now_local (); + self->days_model = g_list_store_new (GCAL_TYPE_AGENDA_VIEW_DAY); - gtk_list_box_set_sort_func (GTK_LIST_BOX (self->list_box), sort_func, self, NULL); - gtk_list_box_set_header_func (GTK_LIST_BOX (self->list_box), update_header, self, NULL); + for (size_t i = 0; i < N_WEEKDAYS; i++) + { + g_autoptr (GcalAgendaViewDay) day = NULL; + g_autoptr (GDateTime) date = NULL; + + date = g_date_time_add_days (self->date, i); + + day = gcal_agenda_view_day_new (); + gcal_agenda_view_day_set_date (day, date); + + g_list_store_append (self->days_model, day); + } + + gtk_filter_list_model_set_model (self->filtered_days, G_LIST_MODEL (self->days_model)); } diff --git a/src/gui/views/meson.build b/src/gui/views/meson.build index 4cbae4f8..cbabc8a6 100644 --- a/src/gui/views/meson.build +++ b/src/gui/views/meson.build @@ -4,6 +4,7 @@ blueprints = custom_target( 'blueprints', input: files( 'gcal-agenda-view.blp', + 'gcal-agenda-view-day-row.blp', 'gcal-month-cell.blp', 'gcal-month-popover.blp', 'gcal-month-view.blp', @@ -29,6 +30,8 @@ built_sources += gnome.compile_resources( sources += files( 'gcal-agenda-view.c', + 'gcal-agenda-view-day.c', + 'gcal-agenda-view-day-row.c', 'gcal-month-cell.c', 'gcal-month-popover.c', 'gcal-month-view.c', diff --git a/src/gui/views/views.gresource.xml b/src/gui/views/views.gresource.xml index e1c21c87..b70d8fb0 100644 --- a/src/gui/views/views.gresource.xml +++ b/src/gui/views/views.gresource.xml @@ -2,6 +2,7 @@ gcal-agenda-view.ui + gcal-agenda-view-day-row.ui gcal-month-cell.ui gcal-month-popover.ui gcal-month-view.ui diff --git a/src/theme/style.css b/src/theme/style.css index a119e377..d98f5269 100644 --- a/src/theme/style.css +++ b/src/theme/style.css @@ -424,16 +424,24 @@ monthpopover > box { /* Agenda View CSS */ -agenda-view list > row { - padding: 3px; - margin-left: 9px; - margin-right: 9px; - border-radius: 6px; +agenda-view listview > row { background: none; } -agenda-view list > label { - padding: 6px 12px; +agenda-view agendaviewdayrow { + margin-left: 9px; + margin-right: 9px; + margin-bottom: 3px; +} + +agenda-view agendaviewdayrow > box > label { + padding: 6px 0; +} + +agenda-view agendaviewdayrow list > row, +agenda-view agendaviewdayrow list > label { + border-radius: 6px; + background: none; } .sidebar-pane agenda-view .background {