From 06aab9952aaec850b4920b803dcd8693b2d135bc Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Wed, 20 Apr 2022 15:19:23 +0200 Subject: [PATCH] Add GcalAgendaView --- po/POTFILES.in | 1 + src/gui/views/gcal-agenda-view.c | 743 ++++++++++++++++++++++++++++++ src/gui/views/gcal-agenda-view.h | 29 ++ src/gui/views/gcal-agenda-view.ui | 51 ++ src/gui/views/meson.build | 1 + src/gui/views/views.gresource.xml | 1 + src/theme/Adwaita.css | 14 + 7 files changed, 840 insertions(+) create mode 100644 src/gui/views/gcal-agenda-view.c create mode 100644 src/gui/views/gcal-agenda-view.h create mode 100644 src/gui/views/gcal-agenda-view.ui diff --git a/po/POTFILES.in b/po/POTFILES.in index cf9e2e88..2672a876 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -39,6 +39,7 @@ src/gui/importer/gcal-import-dialog.c src/gui/importer/gcal-import-dialog.ui src/gui/importer/gcal-importer.c src/gui/importer/gcal-import-file-row.c +src/gui/views/gcal-agenda-view.c src/gui/views/gcal-month-popover.ui src/gui/views/gcal-week-grid.c src/gui/views/gcal-week-header.c diff --git a/src/gui/views/gcal-agenda-view.c b/src/gui/views/gcal-agenda-view.c new file mode 100644 index 00000000..e817155f --- /dev/null +++ b/src/gui/views/gcal-agenda-view.c @@ -0,0 +1,743 @@ +/* gcal-agenda-view.c + * + * Copyright (C) 2022 Purism SPC + * + * 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 . + */ + +#define G_LOG_DOMAIN "GcalAgendaView" + +#include "gcal-agenda-view.h" +#include "gcal-debug.h" +#include "gcal-enums.h" +#include "gcal-event-widget.h" +#include "gcal-range-tree.h" +#include "gcal-timeline-subscriber.h" +#include "gcal-utils.h" +#include "gcal-view.h" + +#include +#include + +#include + +typedef struct +{ + GtkWidget *widget; + GcalEvent *event; + GcalAgendaView *self; +} ChildData; + +struct _GcalAgendaView +{ + GtkBox parent; + + GtkWidget *list_box; + GtkWidget *no_events_row; + GtkWidget *scrolled_window; + + /* property */ + GDateTime *date; + GcalContext *context; + + GcalRangeTree *events; + guint scroll_grid_timeout_id; + gulong stack_page_changed_id; + + gint events_on_date; + gint clicked_cell; +}; + +static void schedule_position_scroll (GcalAgendaView *self); + +static void gcal_view_interface_init (GcalViewInterface *iface); + +static void gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface); + +enum +{ + PROP_0, + PROP_DATE, + PROP_CONTEXT, + N_PROPS, +}; + + +G_DEFINE_TYPE_WITH_CODE (GcalAgendaView, gcal_agenda_view, GTK_TYPE_BOX, + G_IMPLEMENT_INTERFACE (GCAL_TYPE_VIEW, gcal_view_interface_init) + G_IMPLEMENT_INTERFACE (GCAL_TYPE_TIMELINE_SUBSCRIBER, + gcal_timeline_subscriber_interface_init)); + +/* + * 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 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 + return g_date_time_format (date, "%A %B %e"); +} + + +/* + * Callbacks + */ + +static void +on_event_activated (GcalAgendaView *self, + GtkWidget *widget) +{ + g_signal_emit_by_name (self, "event-activated", widget); +} + +static void +stack_visible_child_changed_cb (AdwViewStack *stack, + GParamSpec *pspec, + GcalAgendaView *self) +{ + if (adw_view_stack_get_visible_child (stack) != (GtkWidget*) self) + return; + + schedule_position_scroll (self); + + 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) +{ + g_autoptr(GDateTime) week_start = NULL; + g_autoptr(GDateTime) week_end = NULL; + g_autoptr(GDateTime) now = NULL; + GtkAdjustment *vadjustment; + gdouble minutes, real_value; + gdouble max, page, page_increment, value; + gboolean dummy; + + /* While the scrolled window is not mapped, we keep waiting */ + if (!gtk_widget_get_realized (self->scrolled_window) || + !gtk_widget_get_mapped (self->scrolled_window)) + { + if (self->stack_page_changed_id == 0) + { + GtkWidget *stack = gtk_widget_get_ancestor (GTK_WIDGET (self), ADW_TYPE_VIEW_STACK); + + self->stack_page_changed_id = g_signal_connect_object (stack, + "notify::visible-child", + G_CALLBACK (stack_visible_child_changed_cb), + self, + 0); + } + + self->scroll_grid_timeout_id = 0; + + GCAL_RETURN (G_SOURCE_REMOVE); + } + + now = g_date_time_new_now_local (); + week_start = gcal_date_time_get_start_of_week (self->date); + week_end = gcal_date_time_get_end_of_week (self->date); + + /* Don't animate when not today */ + if (gcal_date_time_compare_date (now, week_start) < 0 || gcal_date_time_compare_date (now, week_end) >= 0) + GCAL_GOTO (out); + + vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (self->scrolled_window)); + minutes = g_date_time_get_hour (now) * 60 + g_date_time_get_minute (now); + page = gtk_adjustment_get_page_size (vadjustment); + max = gtk_adjustment_get_upper (vadjustment); + + real_value = max / MINUTES_PER_DAY * minutes - (page / 2.0); + page_increment = gtk_adjustment_get_page_increment (vadjustment); + value = gtk_adjustment_get_value (vadjustment); + + gtk_adjustment_set_page_increment (vadjustment, real_value - value); + + g_signal_emit_by_name (self->scrolled_window, + "scroll-child", + GTK_SCROLL_PAGE_FORWARD, + FALSE, + &dummy); + + gtk_adjustment_set_page_increment (vadjustment, page_increment); + +out: + self->scroll_grid_timeout_id = 0; + GCAL_RETURN (G_SOURCE_REMOVE); +} + +static void +schedule_position_scroll (GcalAgendaView *self) +{ + /* Nothing is scheduled, we should do it now */ + if (self->scroll_grid_timeout_id > 0) + g_source_remove (self->scroll_grid_timeout_id); + + self->scroll_grid_timeout_id = g_timeout_add (200, + (GSourceFunc) update_grid_scroll_position, + self); +} + +static int +sort_func (GtkListBoxRow *row1, + GtkListBoxRow *row2, + gpointer user_data) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (user_data); + gboolean multiday1, multiday2; + int result; + + result = gcal_date_time_compare_date (get_start_date_for_row (self, row1), + get_start_date_for_row (self, row2)); + + if (result != 0) + return result; + + multiday1 = get_row_is_multiday (row1); + multiday2 = get_row_is_multiday (row2); + + /* Sort multi day events before multi single day events of the same day. */ + if (multiday1 != multiday2) + return multiday1 ? -1 : 1; + + return gcal_date_time_compare_date (get_end_date_for_row (self, row1), + get_end_date_for_row (self, row2)); +} + +static void +update_header (GtkListBoxRow *row, + GtkListBoxRow *before, + gpointer user_data) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (user_data); + GDateTime *start = get_start_date_for_row (self, row); + GDateTime *end = get_end_date_for_row (self, row); + g_autofree gchar *label = NULL; + GtkWidget *header; + g_autoptr (GDateTime) today = NULL; + + today = g_date_time_new_now_local (); + + if (before) + { + GDateTime *before_start = get_start_date_for_row (self, before); + GDateTime *before_end = get_end_date_for_row (self, before); + + 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; + } + + if (gcal_date_time_compare_date (start, before_start) == 0 && + gcal_date_time_compare_date (end, before_end) == 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, + NULL); + gtk_widget_add_css_class (header, "caption-heading"); + + gtk_list_box_row_set_header (row, header); +} + + +/* + * GcalView interface + */ + +static GDateTime* +gcal_agenda_view_get_date (GcalView *view) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (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; + + if (gcal_date_time_compare_date (self->date, gcal_event_get_date_start (child_data->event)) == 0) + self->events_on_date++; + + return FALSE; +} + +static void +gcal_agenda_view_set_date (GcalView *view, + GDateTime *date) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (view); + + GCAL_ENTRY; + + gcal_set_date_time (&self->date, 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)); + + GCAL_EXIT; +} + +static GList* +gcal_agenda_view_get_children_by_uuid (GcalView *view, + GcalRecurrenceModType mod, + const gchar *uuid) +{ + GCAL_ENTRY; + + /* FIXME Not sure what to do here. */ + + GCAL_RETURN (NULL); +} + +static void +gcal_agenda_view_clear_marks (GcalView *view) +{ + /* FIXME Not sure what to do here. */ +} + +static GDateTime* +gcal_agenda_view_get_next_date (GcalView *view) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (view); + + g_assert (self->date != NULL); + return g_date_time_add_weeks (self->date, 1); +} + + +static GDateTime* +gcal_agenda_view_get_previous_date (GcalView *view) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (view); + + g_assert (self->date != NULL); + return g_date_time_add_weeks (self->date, -1); +} + +static void +gcal_view_interface_init (GcalViewInterface *iface) +{ + iface->get_date = gcal_agenda_view_get_date; + iface->set_date = gcal_agenda_view_set_date; + iface->get_children_by_uuid = gcal_agenda_view_get_children_by_uuid; + iface->clear_marks = gcal_agenda_view_clear_marks; + iface->get_next_date = gcal_agenda_view_get_next_date; + iface->get_previous_date = gcal_agenda_view_get_previous_date; +} + + +/* + * GcalTimelineSubscriber iface + */ + +static GcalRange* +gcal_agenda_view_get_range (GcalTimelineSubscriber *subscriber) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (subscriber); + + return gcal_range_new_take (g_date_time_ref (self->date), + gcal_date_time_get_end_of_week (self->date), + GCAL_RANGE_DEFAULT); +} + +static void +gcal_agenda_view_add_event (GcalTimelineSubscriber *subscriber, + GcalEvent *event) +{ + GcalAgendaView *self = GCAL_AGENDA_VIEW (subscriber); + 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); + + if (gcal_date_time_compare_date (self->date, gcal_event_get_date_start (event)) == 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, + "context", self->context, + "event", event, + "orientation", GTK_ORIENTATION_VERTICAL, + "timestamp-policy", timestamp_policy, + NULL); + + row = g_object_new (GTK_TYPE_LIST_BOX_ROW, + "activatable", FALSE, + "child", widget, + NULL); + + gcal_range_tree_add_range (self->events, + gcal_event_get_range (event), + child_data_new (row, event, self)); + + /* FIXME */ + /* 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); + GPtrArray *widgets; + guint i; + + GCAL_ENTRY; + + widgets = gcal_range_tree_get_all_data (self->events); + + if (gcal_date_time_compare_date (self->date, gcal_event_get_date_start (event)) == 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 +gcal_agenda_view_update_event (GcalTimelineSubscriber *subscriber, + GcalEvent *event) +{ + GCAL_ENTRY; + + gcal_agenda_view_remove_event (subscriber, event); + gcal_agenda_view_add_event (subscriber, event); + + GCAL_EXIT; +} + +static void +gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface) +{ + iface->get_range = gcal_agenda_view_get_range; + iface->add_event = gcal_agenda_view_add_event; + iface->update_event = gcal_agenda_view_update_event; + iface->remove_event = gcal_agenda_view_remove_event; +} + + +/* + * GObject overrides + */ + +static void +gcal_agenda_view_dispose (GObject *object) +{ + GcalAgendaView *self; + + self = GCAL_AGENDA_VIEW (object); + + 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); + + g_clear_pointer (&self->date, g_date_time_unref); + + g_clear_object (&self->context); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (gcal_agenda_view_parent_class)->finalize (object); +} + +static void +gcal_agenda_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalAgendaView *self = (GcalAgendaView *) object; + + switch (property_id) + { + case PROP_DATE: + gcal_view_set_date (GCAL_VIEW (object), g_value_get_boxed (value)); + break; + + case PROP_CONTEXT: + g_assert (self->context == NULL); + self->context = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gcal_agenda_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GcalAgendaView *self; + + g_return_if_fail (GCAL_IS_AGENDA_VIEW (object)); + self = GCAL_AGENDA_VIEW (object); + + switch (property_id) + { + case PROP_DATE: + g_value_set_boxed (value, self->date); + break; + + case PROP_CONTEXT: + g_value_set_object (value, self->context); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gcal_agenda_view_class_init (GcalAgendaViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gcal_agenda_view_dispose; + object_class->finalize = gcal_agenda_view_finalize; + object_class->set_property = gcal_agenda_view_set_property; + object_class->get_property = gcal_agenda_view_get_property; + + g_object_class_override_property (object_class, PROP_DATE, "active-date"); + g_object_class_override_property (object_class, PROP_CONTEXT, "context"); + + 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, scrolled_window); + + gtk_widget_class_bind_template_callback (widget_class, on_event_activated); + + gtk_widget_class_set_css_name (widget_class, "agenda-view"); +} + +static void +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); + + 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); +} diff --git a/src/gui/views/gcal-agenda-view.h b/src/gui/views/gcal-agenda-view.h new file mode 100644 index 00000000..59b215ef --- /dev/null +++ b/src/gui/views/gcal-agenda-view.h @@ -0,0 +1,29 @@ +/* gcal-agenda-view.h + * + * Copyright (C) 2022 Purism SPC + * + * 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 . + */ + +#pragma once + +#include "gcal-manager.h" +#include "gcal-view.h" + +G_BEGIN_DECLS + +#define GCAL_TYPE_AGENDA_VIEW (gcal_agenda_view_get_type ()) +G_DECLARE_FINAL_TYPE (GcalAgendaView, gcal_agenda_view, GCAL, AGENDA_VIEW, GtkBox) + +G_END_DECLS diff --git a/src/gui/views/gcal-agenda-view.ui b/src/gui/views/gcal-agenda-view.ui new file mode 100644 index 00000000..c15a4f12 --- /dev/null +++ b/src/gui/views/gcal-agenda-view.ui @@ -0,0 +1,51 @@ + + + + diff --git a/src/gui/views/meson.build b/src/gui/views/meson.build index 717adf4a..f68b3dc9 100644 --- a/src/gui/views/meson.build +++ b/src/gui/views/meson.build @@ -7,6 +7,7 @@ built_sources += gnome.compile_resources( ) sources += files( + 'gcal-agenda-view.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 dea33d62..e1c21c87 100644 --- a/src/gui/views/views.gresource.xml +++ b/src/gui/views/views.gresource.xml @@ -1,6 +1,7 @@ + gcal-agenda-view.ui gcal-month-cell.ui gcal-month-popover.ui gcal-month-view.ui diff --git a/src/theme/Adwaita.css b/src/theme/Adwaita.css index d09f525c..5538a31f 100644 --- a/src/theme/Adwaita.css +++ b/src/theme/Adwaita.css @@ -373,6 +373,20 @@ monthpopover > box { background: none; } +/* Agenda View CSS */ + +agenda-view list > row { + padding: 2px 12px; +} + +agenda-view list > label { + padding: 6px 12px; +} + +label.no-events { + font-style: italic; +} + /* * Search */