month-view: fix rtl

This removes much of the subtle complexities of the size allocation
function in month view, and finally delivers a readable and working
implementation of the month view.

This also introduces a small code refactoring, and fixes clicking
on a cell on RTL.
This commit is contained in:
Georges Basile Stavracas Neto
2017-11-15 19:03:10 -02:00
parent b638a1537c
commit 54839efbf4
+91 -122
View File
@@ -129,8 +129,6 @@ struct _GcalMonthView
gboolean pending_event_allocation; gboolean pending_event_allocation;
}; };
#define MIRROR(val,start,end) (start + (val / end) * end + (end - val % end))
static void gcal_view_interface_init (GcalViewInterface *iface); static void gcal_view_interface_init (GcalViewInterface *iface);
static void gtk_buildable_interface_init (GtkBuildableIface *iface); static void gtk_buildable_interface_init (GtkBuildableIface *iface);
@@ -165,16 +163,6 @@ static guint signals[NUM_SIGNALS] = { 0, };
* Auxiliary functions * Auxiliary functions
*/ */
static inline gint
real_cell (gint cell,
gboolean rtl)
{
if (cell < 0)
return cell;
return rtl ? MIRROR (cell, 0, 7) - 1 : cell;
}
static inline void static inline void
cancel_selection (GcalMonthView *self) cancel_selection (GcalMonthView *self)
{ {
@@ -302,17 +290,17 @@ get_month_cell_at_position (GcalMonthView *self,
} }
static void static void
get_widget_parts (GcalMonthView *self, calculate_multiday_event_blocks (GcalMonthView *self,
gint first_cell, gint first_cell,
gint last_cell, gint last_cell,
gint natural_height, gint natural_height,
gdouble *vertical_cell_space, gdouble *vertical_cell_space,
gdouble *size_left, gdouble *size_left,
GArray *cells, GArray *cells,
GArray *lengths, GArray *lengths,
GArray *length_visible, GArray *length_visible,
gint *events_at_day, gint *events_at_day,
gint *allocated_events_at_day) gint *allocated_events_at_day)
{ {
gboolean was_visible; gboolean was_visible;
gdouble old_y; gdouble old_y;
@@ -320,6 +308,8 @@ get_widget_parts (GcalMonthView *self,
gint current_part_length; gint current_part_length;
gint i; gint i;
GCAL_ENTRY;
old_y = -1.0; old_y = -1.0;
current_part_length = 0; current_part_length = 0;
was_visible = FALSE; was_visible = FALSE;
@@ -331,6 +321,8 @@ get_widget_parts (GcalMonthView *self,
first_cell = swap; first_cell = swap;
} }
GCAL_TRACE_MSG ("Calculating event blocks from cell %d to cell %d", first_cell, last_cell);
for (i = first_cell; i <= last_cell; i++) for (i = first_cell; i <= last_cell; i++)
{ {
gboolean visible_at_range; gboolean visible_at_range;
@@ -362,6 +354,8 @@ get_widget_parts (GcalMonthView *self,
if (old_y == -1.0 || y != old_y || different_row || was_visible != visible_at_range) if (old_y == -1.0 || y != old_y || different_row || was_visible != visible_at_range)
{ {
GCAL_TRACE_MSG ("Breaking event at cell %d (previous section was %d)", i, current_part_length);
current_part_length = 1; current_part_length = 1;
old_y = y; old_y = y;
@@ -377,6 +371,8 @@ get_widget_parts (GcalMonthView *self,
was_visible = visible_at_range; was_visible = visible_at_range;
} }
GCAL_EXIT;
} }
static void static void
@@ -1487,9 +1483,11 @@ gcal_month_view_size_allocate (GtkWidget *widget,
gint minimum_height; gint minimum_height;
gint header_height; gint header_height;
gint grid_height; gint grid_height;
gint i, j, sw, row; gint i, j;
self = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); GCAL_ENTRY;
self = GCAL_MONTH_VIEW (widget);
/* Allocate the widget */ /* Allocate the widget */
gtk_widget_get_allocation (widget, &old_alloc); gtk_widget_get_allocation (widget, &old_alloc);
@@ -1527,7 +1525,8 @@ gcal_month_view_size_allocate (GtkWidget *widget,
allocation->width == old_alloc.width && allocation->width == old_alloc.width &&
allocation->height == old_alloc.height) allocation->height == old_alloc.height)
{ {
goto out; GCAL_TRACE_MSG ("Events didn't change, stopping here");
GCAL_GOTO (out);
} }
/* Remove every widget' parts, but the master widget */ /* Remove every widget' parts, but the master widget */
@@ -1554,8 +1553,6 @@ gcal_month_view_size_allocate (GtkWidget *widget,
size_left[i] = h; size_left[i] = h;
} }
sw = 1 - 2 * self->k;
/* /*
* Allocate multidays events before single day events, as they have a * Allocate multidays events before single day events, as they have a
* higher priority. Multiday events have a master event, and clones * higher priority. Multiday events have a master event, and clones
@@ -1563,15 +1560,16 @@ gcal_month_view_size_allocate (GtkWidget *widget,
*/ */
for (l = self->multi_cell_children; l; l = g_list_next (l)) for (l = self->multi_cell_children; l; l = g_list_next (l))
{ {
g_autoptr (GDateTime) start_date = NULL;
g_autoptr (GDateTime) end_date = NULL;
g_autoptr (GArray) visible_at_length = NULL; g_autoptr (GArray) visible_at_length = NULL;
g_autoptr (GArray) lengths = NULL; g_autoptr (GArray) lengths = NULL;
g_autoptr (GArray) cells = NULL; g_autoptr (GArray) blocks = NULL;
GDateTime *date;
GcalEvent *event; GcalEvent *event;
gboolean all_day; gboolean all_day;
gint first_cell, last_cell; gint event_block;
gint first_row, last_row; gint first_cell;
gint cell; gint last_cell;
child_widget = (GtkWidget*) l->data; child_widget = (GtkWidget*) l->data;
event = gcal_event_widget_get_event (l->data); event = gcal_event_widget_get_event (l->data);
@@ -1584,117 +1582,88 @@ gcal_month_view_size_allocate (GtkWidget *widget,
* we're visualizing, the first cell is the event's day of the * we're visualizing, the first cell is the event's day of the
* month. Otherwise, the first cell is the 1st day of the month. * month. Otherwise, the first cell is the 1st day of the month.
*/ */
j = 1; first_cell = 1;
date = gcal_event_get_date_start (event); start_date = gcal_event_get_date_start (event);
date = all_day ? g_date_time_ref (date) : g_date_time_to_local (date); start_date = all_day ? g_date_time_ref (start_date) : g_date_time_to_local (start_date);
if (g_date_time_get_year (date) == self->date->year && if (g_date_time_get_year (start_date) == self->date->year &&
g_date_time_get_month (date) == self->date->month) g_date_time_get_month (start_date) == self->date->month)
{ {
j = g_date_time_get_day_of_month (date); first_cell = g_date_time_get_day_of_month (start_date);
} }
j += self->days_delay; first_cell += self->days_delay - 1;
g_clear_pointer (&date, g_date_time_unref);
/*
* Calculate the first cell position according to the locale
* and the start date.
*/
first_cell = 7 * ((j - 1) / 7)+ 6 * self->k + sw * ((j - 1) % 7);
/* /*
* The logic for the end date is the same, except that we have to check * The logic for the end date is the same, except that we have to check
* if the event is all day or not. * if the event is all day or not.
*/ */
j = icaltime_days_in_month (self->date->month, self->date->year); last_cell = icaltime_days_in_month (self->date->month, self->date->year);
date = gcal_event_get_date_end (event); end_date = gcal_event_get_date_end (event);
date = all_day ? g_date_time_ref (date) : g_date_time_to_local (date); end_date = all_day ? g_date_time_ref (end_date) : g_date_time_to_local (end_date);
if (g_date_time_get_month (date) == self->date->month) if (g_date_time_get_month (end_date) == self->date->month)
{ {
j = g_date_time_get_day_of_month (date); last_cell = g_date_time_get_day_of_month (end_date);
/* If the event is all day, we have to subtract 1 to find the the real date */ /* If the event is all day, we have to subtract 1 to find the the real date */
if (all_day) if (all_day)
j--; last_cell--;
} }
j += self->days_delay; last_cell += self->days_delay - 1;
g_clear_pointer (&date, g_date_time_unref);
last_cell = 7 * ((j - 1) / 7)+ 6 * self->k + sw * ((j - 1) % 7);
/* /*
* Now that we found the start & end dates, we have to find out * Now that we found the start & end dates, we have to find out
* whether the event is visible or not. * whether the event is visible or not.
*/ */
first_row = first_cell / 7;
last_row = last_cell / 7; GCAL_TRACE_MSG ("Positioning '%s' (multiday) from %d to %d", gcal_event_get_summary (event), first_cell, last_cell);
cells = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16);
blocks = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16);
lengths = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16); lengths = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16);
visible_at_length = g_array_sized_new (TRUE, TRUE, sizeof (gboolean), 16); visible_at_length = g_array_sized_new (TRUE, TRUE, sizeof (gboolean), 16);
for (row = first_row; row <= last_row; row++) /* Position and allocate the child widget */
{ minimum_height = get_real_event_widget_height (child_widget);
gint start, end;
/* Position and allocate the child widget */ gtk_style_context_get_margin (child_context,
minimum_height = get_real_event_widget_height (child_widget); gtk_style_context_get_state (child_context),
&margin);
gtk_style_context_get_margin (gtk_widget_get_style_context (child_widget), calculate_multiday_event_blocks (self,
gtk_style_context_get_state (child_context), first_cell, last_cell,
&margin); minimum_height + margin.top + margin.bottom,
vertical_cell_space,
size_left,
blocks,
lengths,
visible_at_length,
events_at_day,
allocated_events_at_day);
start = row * 7 + self->k * 6; for (event_block = 0; event_block < blocks->len; event_block++)
end = row * 7 + (1 - self->k) * 6;
if (row == first_row)
start = first_cell;
if (row == last_row)
end = last_cell;
get_widget_parts (self,
start, end,
minimum_height + margin.top + margin.bottom,
vertical_cell_space,
size_left,
cells,
lengths,
visible_at_length,
events_at_day,
allocated_events_at_day);
}
for (cell = 0; cell < cells->len; cell++)
{ {
g_autoptr (GDateTime) dt_start = NULL; g_autoptr (GDateTime) dt_start = NULL;
g_autoptr (GDateTime) dt_end = NULL; g_autoptr (GDateTime) dt_end = NULL;
gboolean visible; gboolean visible;
gdouble width; gdouble width;
gint cell_idx; gint last_block_cell;
gint length; gint length;
gint column; gint cell;
gint day; gint day;
visible = g_array_index (visible_at_length, gboolean, cell); visible = g_array_index (visible_at_length, gboolean, event_block);
cell_idx = g_array_index (cells, gint, cell); length = g_array_index (lengths, gint, event_block);
length = g_array_index (lengths, gint, cell); cell = g_array_index (blocks, gint, event_block);
row = cell_idx / 7; last_block_cell = cell + length - 1;
column = cell_idx % 7; day = cell - self->days_delay + 1;
day = cell_idx - self->days_delay + 1;
/* Day number is calculated differently on RTL languages */
if (self->k)
day = 7 * row + MIRROR (cell_idx % 7, 0, 7) - self->days_delay - length + 1;
/* /*
* Setup a new event widget when the cell index changes. This happens when * Setup a new event widget when the cell index changes. This happens when
* the event has a different y position, or when the row changed. * the event has a different y position, or when the row changed.
*/ */
if (cell != 0) if (event_block != 0)
{ {
child_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (child_widget)); child_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (child_widget));
gtk_widget_show (child_widget); gtk_widget_show (child_widget);
@@ -1714,7 +1683,7 @@ gcal_month_view_size_allocate (GtkWidget *widget,
gtk_widget_set_child_visible (child_widget, FALSE); gtk_widget_set_child_visible (child_widget, FALSE);
for (idx = cell_idx; idx < cell_idx + length; idx++) for (idx = cell; idx < cell + length; idx++)
{ {
aux = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (idx)); aux = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (idx));
aux = g_list_append (aux, child_widget); aux = g_list_append (aux, child_widget);
@@ -1728,11 +1697,15 @@ gcal_month_view_size_allocate (GtkWidget *widget,
continue; continue;
} }
/* Retrieve the cell widget */
/*
* Retrieve the cell widget. On RTL languages, we use the last month cell as the starting
* point.
*/
if (self->k) if (self->k)
month_cell = self->month_cell[row][6 - column]; month_cell = self->month_cell[last_block_cell / 7][last_block_cell % 7];
else else
month_cell = self->month_cell[row][column]; month_cell = self->month_cell[cell / 7][cell % 7];
gtk_widget_get_allocation (month_cell, &cell_alloc); gtk_widget_get_allocation (month_cell, &cell_alloc);
@@ -1774,7 +1747,7 @@ gcal_month_view_size_allocate (GtkWidget *widget,
minimum_height = get_real_event_widget_height (child_widget); minimum_height = get_real_event_widget_height (child_widget);
child_allocation.x = pos_x; child_allocation.x = pos_x;
child_allocation.y = pos_y + vertical_cell_space[cell_idx] - size_left[cell_idx]; child_allocation.y = pos_y + vertical_cell_space[cell] - size_left[cell];
child_allocation.width = width - (margin.left + margin.right); child_allocation.width = width - (margin.left + margin.right);
child_allocation.height = minimum_height; child_allocation.height = minimum_height;
@@ -1783,8 +1756,8 @@ gcal_month_view_size_allocate (GtkWidget *widget,
/* update size_left */ /* update size_left */
for (j = 0; j < length; j++) for (j = 0; j < length; j++)
{ {
size_left[cell_idx + j] -= minimum_height; size_left[cell + j] -= minimum_height;
size_left[cell_idx + j] -= margin.top + margin.bottom; size_left[cell + j] -= margin.top + margin.bottom;
} }
} }
} }
@@ -1800,15 +1773,11 @@ gcal_month_view_size_allocate (GtkWidget *widget,
{ {
gboolean will_overflow; gboolean will_overflow;
gint cell; gint cell;
gint day;
j = GPOINTER_TO_INT (key); day = GPOINTER_TO_INT (key);
j += self->days_delay; cell = day + self->days_delay - 1;
cell = 7 * ((j - 1) / 7) + 6 * self->k + sw * ((j - 1) % 7); month_cell = self->month_cell[cell / 7][cell % 7];
if (self->k)
month_cell = self->month_cell[cell / 7][6 - cell % 7];
else
month_cell = self->month_cell[cell / 7][cell % 7];
gtk_widget_get_allocation (month_cell, &cell_alloc); gtk_widget_get_allocation (month_cell, &cell_alloc);
@@ -1834,7 +1803,7 @@ gcal_month_view_size_allocate (GtkWidget *widget,
real_height = size_left[cell]; real_height = size_left[cell];
if (will_overflow) if (will_overflow)
real_height -= gcal_month_cell_get_overflow_height (GCAL_MONTH_CELL (self->month_cell[cell / 7][cell % 7])); real_height -= gcal_month_cell_get_overflow_height (GCAL_MONTH_CELL (month_cell));
/* No space left, add to the overflow and continue */ /* No space left, add to the overflow and continue */
if (real_height < minimum_height) if (real_height < minimum_height)
@@ -1878,6 +1847,8 @@ out:
queue_update_month_cells (self); queue_update_month_cells (self);
self->pending_event_allocation = FALSE; self->pending_event_allocation = FALSE;
GCAL_EXIT;
} }
static gboolean static gboolean
@@ -1899,8 +1870,6 @@ gcal_month_view_button_press (GtkWidget *widget,
get_month_cell_at_position (self, x, y, &clicked_cell); get_month_cell_at_position (self, x, y, &clicked_cell);
clicked_cell = real_cell (clicked_cell, self->k);
if (clicked_cell >= self->days_delay && clicked_cell < days) if (clicked_cell >= self->days_delay && clicked_cell < days)
{ {
g_clear_pointer (&self->start_mark_cell, g_date_time_unref); g_clear_pointer (&self->start_mark_cell, g_date_time_unref);