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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user