views/agenda: Use ListView item factory for creating event widgets

Instead of having the `GtkListView` create a row for each day containing
their own internal `GtkListBox` to store the event widgets, move the
creation of event widgets to the `GtkListView`'s item factory.

The motivation for this change is having internal listbox's inside a
`GtkListView` is problematic for keyboard navigation, because the user
can either:

- The event widgets in the internal listbox's can not be activated by
  pressing Space/Enter on a focused event widget if the listbox row's are set to be focusable.
- If the internal listbox's rows are set to be unfocusable, the user is
  not able to navigate the event widgets using the arrow keys, Page
  Up/Down or Home/End.

To use `GtkListView:factory` to generate the event widgets, we first
wrap the filtered day model inside the list view around a
`GtkFlattenListModel` to concatenate all the containing
`GcalAgendaViewItem` (which contain a given event and the date it occurs
on) in the day models together. This gives us a model of all events that
can be turned into event widgets, and a model of day sections to create
headers for each day in the next commit.

This approach was taken because we do not get the activated row widget,
unlike with the previously used `GtkListBox` where the row widget is
passed as an argument in `::row-activated`, and there is no feasible
other way to obtain the correct event widget from a `GtkListView`.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/809>
This commit is contained in:
Zelda Ahmed
2026-09-26 13:02:27 +01:00
parent 3f1bc3edf0
commit 55d9cc68bc
3 changed files with 57 additions and 24 deletions
+1
View File
@@ -65,6 +65,7 @@ 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.c
src/gui/views/gcal-agenda-view-day-row.blp
src/gui/views/gcal-agenda-view-day-row.c
src/gui/views/gcal-month-cell.blp
+16 -10
View File
@@ -21,14 +21,16 @@ template $GcalAgendaView: Box {
single-click-activate: true;
model: Gtk.NoSelection {
model: FilterListModel filtered_days {
watch-items: true;
model: FlattenListModel flatten_model {
model: FilterListModel filtered_days {
watch-items: true;
filter: BoolFilter {
expression: expr $n_items_and_date_to_boolean(
item as <$GcalAgendaViewDay>.n-items,
item as <$GcalAgendaViewDay>.date,
) as <bool>;
filter: BoolFilter {
expression: expr $n_items_and_date_to_boolean(
item as <$GcalAgendaViewDay>.n-items,
item as <$GcalAgendaViewDay>.date,
) as <bool>;
};
};
};
};
@@ -39,9 +41,13 @@ template $GcalAgendaView: Box {
selectable: false;
focusable: false;
child: $GcalAgendaViewDayRow {
day: bind template.item;
event-activated => $on_day_row_event_activated_cb(template) not-swapped;
child: $GcalEventWidget {
event: bind template.item as <$GcalAgendaViewItem>.event;
orientation: bind $orientation_from_event(template.item as <$GcalAgendaViewItem>.event) as <Orientation>;
timestamp-policy: bind $timestamp_policy_from_event(
template.item as <$GcalAgendaViewItem>.event
) as <$GcalTimestampPolicy>;
activate => $on_event_widget_activated_cb(template) not-swapped;
};
}
};
+40 -14
View File
@@ -31,6 +31,7 @@
#include "gcal-view-private.h"
#include <adwaita.h>
#include <glib/gi18n.h>
#include <math.h>
@@ -51,6 +52,7 @@ struct _GcalAgendaView
GDateTime *date;
GListStore *days_model;
GtkFilterListModel *filtered_days;
GtkFlattenListModel *flatten_model;
guint scroll_grid_timeout_id;
gulong stack_page_changed_id;
@@ -202,6 +204,40 @@ n_items_and_date_to_boolean (GcalAgendaViewDay *day,
return FALSE;
}
static GcalTimestampPolicy
timestamp_policy_from_event (GtkListItem *item,
GcalEvent *event)
{
gboolean is_multiday_or_all_day;
if (!event)
return GCAL_TIMESTAMP_POLICY_NONE;
is_multiday_or_all_day = (gcal_event_get_all_day (event) || gcal_event_is_multiday (event));
return is_multiday_or_all_day ? GCAL_TIMESTAMP_POLICY_END : GCAL_TIMESTAMP_POLICY_START;
}
static GtkOrientation
orientation_from_event (GtkListItem *item,
GcalEvent *event)
{
gboolean is_multiday_or_all_day = TRUE;
if (event)
is_multiday_or_all_day = (gcal_event_get_all_day (event) || gcal_event_is_multiday (event));
return is_multiday_or_all_day ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
}
static void
on_event_widget_activated_cb (GcalEventWidget *event_widget,
GtkListItem *item)
{
GcalView *view = GCAL_VIEW (gtk_widget_get_ancestor (GTK_WIDGET (event_widget), GCAL_TYPE_VIEW));
gcal_view_event_activated (view, event_widget);
}
/*
* GcalView interface
@@ -296,19 +332,6 @@ gcal_view_interface_init (GcalViewInterface *iface)
* GcalTimelineSubscriber iface
*/
static void
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 GcalRange*
gcal_agenda_view_get_range (GcalTimelineSubscriber *subscriber)
{
@@ -436,11 +459,14 @@ 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, flatten_model);
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, n_items_and_date_to_boolean);
gtk_widget_class_bind_template_callback (widget_class, on_day_row_event_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, on_event_widget_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, orientation_from_event);
gtk_widget_class_bind_template_callback (widget_class, timestamp_policy_from_event);
gtk_widget_class_set_css_name (widget_class, "agenda-view");
}