From 03e6e39433f061ce7f77839ea10fe2414ecb735d Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 11 Mar 2025 15:23:16 -0300 Subject: [PATCH] views/month: Rebuild layout blocks at once per frame Instead of rebuilding them all every time an event is added or removed, rebuild it once per frame. In case the month view isn't visible, don't do anything, and wait until it's mapped to actually rebuild the event blocks. Related: https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1381 Related: https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1125 --- src/gui/views/gcal-month-view-row.c | 71 ++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/gui/views/gcal-month-view-row.c b/src/gui/views/gcal-month-view-row.c index 8a48a3e8..43e51b8d 100644 --- a/src/gui/views/gcal-month-view-row.c +++ b/src/gui/views/gcal-month-view-row.c @@ -51,6 +51,7 @@ struct _GcalMonthViewRow GListStore *events; GHashTable *layout_blocks; + gboolean layout_blocks_valid; GcalContext *context; }; @@ -62,6 +63,10 @@ static gint compare_events_cb (gconstpointer static void on_event_widget_activated_cb (GcalEventWidget *widget, GcalMonthViewRow *self); +static gboolean widget_tick_cb (GtkWidget *widget, + GdkFrameClock *frame_clock, + gpointer user_data); + G_DEFINE_FINAL_TYPE (GcalMonthViewRow, gcal_month_view_row, GTK_TYPE_WIDGET) enum @@ -173,6 +178,8 @@ prepare_layout_blocks (GcalMonthViewRow *self, guint weekday_heights[7] = { 0, }; guint n_events; + g_assert (self->layout_blocks_valid); + n_events = g_list_model_get_n_items (G_LIST_MODEL (self->events)); for (guint i = 0; i < 7; i++) @@ -274,6 +281,10 @@ recalculate_layout_blocks (GcalMonthViewRow *self) guint events_at_weekday[7] = { 0, }; guint n_events; + GCAL_ENTRY; + + g_assert (!self->layout_blocks_valid); + range_start = gcal_range_get_start (self->range); n_events = g_list_model_get_n_items (G_LIST_MODEL (self->events)); @@ -363,6 +374,29 @@ recalculate_layout_blocks (GcalMonthViewRow *self) g_hash_table_insert (self->layout_blocks, event, g_steal_pointer (&blocks)); } + + self->layout_blocks_valid = TRUE; + + GCAL_EXIT; +} + +static void +invalidate_layout_blocks (GcalMonthViewRow *self) +{ + GCAL_ENTRY; + + if (!self->layout_blocks_valid) + GCAL_RETURN (); + + if (gtk_widget_get_mapped (GTK_WIDGET (self))) + { + gtk_widget_add_tick_callback (GTK_WIDGET (self), widget_tick_cb, self, NULL); + gtk_widget_queue_allocate (GTK_WIDGET (self)); + } + + self->layout_blocks_valid = FALSE; + + GCAL_EXIT; } @@ -399,11 +433,40 @@ on_month_cell_show_overflow_cb (GcalMonthCell *cell, g_signal_emit (self, signals[SHOW_OVERFLOW], 0, cell); } +static gboolean +widget_tick_cb (GtkWidget *widget, + GdkFrameClock *frame_clock, + gpointer user_data) +{ + GcalMonthViewRow *self = (GcalMonthViewRow *) widget; + + GCAL_ENTRY; + + g_assert (GCAL_IS_MONTH_VIEW_ROW (self)); + + recalculate_layout_blocks (self); + + GCAL_RETURN (G_SOURCE_REMOVE); +} + /* * GtkWidget overrides */ +static void +gcal_month_view_row_map (GtkWidget *widget) +{ + GcalMonthViewRow *self = (GcalMonthViewRow *) widget; + + g_assert (GCAL_IS_MONTH_VIEW_ROW (self)); + + GTK_WIDGET_CLASS (gcal_month_view_row_parent_class)->map (widget); + + if (!self->layout_blocks_valid) + recalculate_layout_blocks (self); +} + static void gcal_month_view_row_measure (GtkWidget *widget, GtkOrientation orientation, @@ -485,6 +548,7 @@ gcal_month_view_row_size_allocate (GtkWidget *widget, } /* Event widgets */ + if (self->layout_blocks_valid) { gdouble cell_y[7]; guint overflows[7] = { 0, }; @@ -610,6 +674,7 @@ gcal_month_view_row_class_init (GcalMonthViewRowClass *klass) object_class->get_property = gcal_month_view_row_get_property; object_class->set_property = gcal_month_view_row_set_property; + widget_class->map = gcal_month_view_row_map; widget_class->measure = gcal_month_view_row_measure; widget_class->size_allocate = gcal_month_view_row_size_allocate; @@ -645,6 +710,7 @@ gcal_month_view_row_init (GcalMonthViewRow *self) { self->events = g_list_store_new (GCAL_TYPE_EVENT); self->layout_blocks = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_ptr_array_unref); + self->layout_blocks_valid = TRUE; for (guint i = 0; i < 7; i++) { @@ -712,6 +778,7 @@ gcal_month_view_row_set_range (GcalMonthViewRow *self, /* Preemptively remove all event widgets */ g_list_store_remove_all (self->events); g_hash_table_remove_all (self->layout_blocks); + invalidate_layout_blocks (self); start = gcal_range_get_start (range); for (guint i = 0; i < 7; i++) @@ -729,7 +796,7 @@ gcal_month_view_row_add_event (GcalMonthViewRow *self, g_assert (GCAL_IS_EVENT (event)); g_list_store_insert_sorted (self->events, event, compare_events_cb, self); - recalculate_layout_blocks (self); + invalidate_layout_blocks (self); } void @@ -753,7 +820,7 @@ gcal_month_view_row_remove_event (GcalMonthViewRow *self, } g_list_store_remove (self->events, position); - recalculate_layout_blocks (self); + invalidate_layout_blocks (self); GCAL_EXIT; }