From 93acf26507efc3052381840f1d04f6f7079751a3 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 9 Dec 2016 00:03:21 -0200 Subject: [PATCH] week-grid: properly position overlapping events When two or more events overlap, the week grid almost does the right thing and correctly calculates the width of the events. It fails, however, in calculating the position of the events. This commit introduces a simple yet efficient way to position the events. It uses a temporary range tree to efficiently store already positioned widgets, and checks for their indexes looking for a hole to position the new event. --- src/views/gcal-week-grid.c | 89 +++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/src/views/gcal-week-grid.c b/src/views/gcal-week-grid.c index 55bdbd3c..c2ff9452 100644 --- a/src/views/gcal-week-grid.c +++ b/src/views/gcal-week-grid.c @@ -164,6 +164,65 @@ get_event_range (GcalWeekGrid *self, g_clear_pointer (&week_start, g_date_time_unref); } +static inline gint +int16_compare (gconstpointer a, + gconstpointer b) +{ + return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b); +} + +static inline guint +get_event_index (GcalRangeTree *tree, + guint16 start, + guint16 end) +{ + g_autoptr (GPtrArray) array; + gint idx, i; + + i = idx = 0; + array = gcal_range_tree_get_data_at_range (tree, start, end); + + if (!array) + return 0; + + g_ptr_array_sort (array, int16_compare); + + for (i = 0; array && i < array->len; i++) + { + if (idx == GPOINTER_TO_INT (g_ptr_array_index (array, i))) + idx++; + else + break; + } + + + return idx; +} + +static guint +count_overlaps_at_range (GcalRangeTree *self, + guint16 start, + guint16 end) +{ + guint64 i, counter; + + g_return_val_if_fail (self, 0); + g_return_val_if_fail (end >= start, 0); + + counter = 0; + + for (i = start; i < end; i++) + { + guint n_events; + + n_events = gcal_range_tree_count_entries_at_range (self, i, i + 1); + + counter = MAX (counter, n_events); + } + + return counter; +} + static void gcal_week_grid_finalize (GObject *object) { @@ -492,6 +551,7 @@ gcal_week_grid_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GcalWeekGrid *self = GCAL_WEEK_GRID (widget); + GcalRangeTree *overlaps; gboolean ltr; gdouble minutes_height; gdouble column_width; @@ -525,6 +585,9 @@ gcal_week_grid_size_allocate (GtkWidget *widget, minutes_height = (gdouble) allocation->height / MINUTES_PER_DAY; column_width = (gdouble) allocation->width / 7.0; + /* Temporary range tree to hold positioned events' indexes */ + overlaps = gcal_range_tree_new (); + /* * Iterate through weekdays; we don't have to worry about events that * jump between days because they're already handled by GcalWeekHeader. @@ -549,6 +612,8 @@ gcal_week_grid_size_allocate (GtkWidget *widget, GtkBorder margin; guint64 events_at_range; gint natural_height; + gint widget_index; + gint offset; gint height; gint width; @@ -556,9 +621,11 @@ gcal_week_grid_size_allocate (GtkWidget *widget, event_widget = data->widget; context = gtk_widget_get_style_context (event_widget); - events_at_range = gcal_range_tree_count_entries_at_range (self->events, - data->start, - data->end); + /* The total number of events available in this range */ + events_at_range = count_overlaps_at_range (self->events, data->start, data->end); + + /* The real horizontal position of this event */ + widget_index = get_event_index (overlaps, data->start, data->end); /* Gtk complains about that */ gtk_widget_get_preferred_height (event_widget, NULL, &natural_height); @@ -570,11 +637,12 @@ gcal_week_grid_size_allocate (GtkWidget *widget, width = column_width / events_at_range - margin.left - margin.right; height = MAX ((data->end - data->start) * minutes_height - margin.top - margin.bottom, natural_height); + offset = (width + margin.left + margin.right) * widget_index; if (ltr) - x = column_width * i + allocation->x + margin.left + 1; + x = column_width * i + offset + allocation->x + margin.left + 1; else - x = allocation->width - width - (column_width * i + allocation->x + margin.left + 1); + x = allocation->width - width - (column_width * i + offset + allocation->x + margin.left + 1); /* Setup the child position and size */ child_allocation.x = x; @@ -583,12 +651,23 @@ gcal_week_grid_size_allocate (GtkWidget *widget, child_allocation.height = height; gtk_widget_size_allocate (event_widget, &child_allocation); + + /* + * Add the current event to the temporary overlaps tree so we have a way to + * know how many events are already positioned in the current column. + */ + gcal_range_tree_add_range (overlaps, + data->start, + data->end, + GINT_TO_POINTER (widget_index)); } g_clear_pointer (&widgets_data, g_ptr_array_unref); } self->children_changed = FALSE; + + g_clear_pointer (&overlaps, gcal_range_tree_unref); } static void