[gnome-calendar/gbsneto/gtk4: 3/13] week-view: Port to GTK4




commit 27abf12d0b5f3b31725b4e958bd1b7234181404b
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Sun Jan 23 19:48:47 2022 -0300

    week-view: Port to GTK4

 src/gui/views/gcal-week-grid.c        | 830 ++++++++++++++--------------------
 src/gui/views/gcal-week-grid.h        |   2 +-
 src/gui/views/gcal-week-header.c      | 758 ++++++++++++-------------------
 src/gui/views/gcal-week-header.h      |   2 +-
 src/gui/views/gcal-week-header.ui     | 143 ++----
 src/gui/views/gcal-week-hour-bar.c    | 144 ++++++
 src/gui/views/gcal-week-hour-bar.h    |  35 ++
 src/gui/views/gcal-week-view-common.c |  87 ++++
 src/gui/views/gcal-week-view-common.h |  34 ++
 src/gui/views/gcal-week-view.c        | 213 +--------
 src/gui/views/gcal-week-view.ui       |  10 +-
 src/gui/views/meson.build             |   2 +
 src/theme/Adwaita.css                 |  33 +-
 src/utils/gcal-utils.c                |  26 +-
 src/utils/gcal-utils.h                |   2 +-
 15 files changed, 1001 insertions(+), 1320 deletions(-)
---
diff --git a/src/gui/views/gcal-week-grid.c b/src/gui/views/gcal-week-grid.c
index 8df2a5e4..e4bc29cf 100644
--- a/src/gui/views/gcal-week-grid.c
+++ b/src/gui/views/gcal-week-grid.c
@@ -25,6 +25,7 @@
 #include "gcal-debug.h"
 #include "gcal-week-grid.h"
 #include "gcal-week-view.h"
+#include "gcal-week-view-common.h"
 #include "gcal-utils.h"
 #include "gcal-view.h"
 #include "gcal-event-widget.h"
@@ -34,12 +35,6 @@
 #include <string.h>
 #include <math.h>
 
-static const double dashed [] =
-{
-  5.0,
-  6.0
-};
-
 typedef struct
 {
   GtkWidget          *widget;
@@ -48,10 +43,11 @@ typedef struct
 
 struct _GcalWeekGrid
 {
-  GtkContainer        parent;
+  GtkWidget           parent;
 
   GtkWidget          *hours_sidebar;
-  GdkWindow          *event_window;
+  GtkWidget          *now_strip;
+  GtkEventController *motion_controller;
 
   GDateTime          *active_date;
 
@@ -68,7 +64,7 @@ struct _GcalWeekGrid
   GcalContext        *context;
 };
 
-G_DEFINE_TYPE (GcalWeekGrid, gcal_week_grid, GTK_TYPE_CONTAINER);
+G_DEFINE_TYPE (GcalWeekGrid, gcal_week_grid, GTK_TYPE_WIDGET);
 
 
 enum
@@ -79,7 +75,10 @@ enum
 
 static guint signals[LAST_SIGNAL] = { 0, };
 
-/* ChildData methods */
+/*
+ * Auxiliary methods
+ */
+
 static ChildData*
 child_data_new (GtkWidget *widget,
                 GcalEvent *event)
@@ -93,31 +92,19 @@ child_data_new (GtkWidget *widget,
   return data;
 }
 
-/* Event activation methods */
 static void
-on_event_widget_activated (GcalEventWidget *widget,
-                           GcalWeekGrid    *self)
+child_data_free (gpointer data)
 {
-  g_signal_emit (self, signals[EVENT_ACTIVATED], 0, widget);
-}
+  ChildData *child_data = data;
 
+  if (!child_data)
+    return;
 
-static inline void
-setup_event_widget (GcalWeekGrid *self,
-                    GtkWidget    *widget)
-{
-  g_signal_connect (widget, "activate", G_CALLBACK (on_event_widget_activated), self);
-}
-
-static inline void
-destroy_event_widget (GcalWeekGrid *self,
-                      GtkWidget    *widget)
-{
-  g_signal_handlers_disconnect_by_func (widget, on_event_widget_activated, self);
-  gtk_widget_destroy (widget);
+  g_clear_pointer (&child_data->widget, gtk_widget_unparent);
+  g_clear_object (&child_data->event);
+  g_free (child_data);
 }
 
-/* Auxiliary methods */
 static inline gint
 uint16_compare (gconstpointer a,
                 gconstpointer b)
@@ -187,156 +174,191 @@ count_overlaps_at_range (GcalRangeTree *self,
   return counter;
 }
 
+/*
+ * Callbacks
+ */
+
 static void
-gcal_week_grid_finalize (GObject *object)
+on_click_gesture_pressed_cb (GtkGestureClick *click_gesture,
+                             gint             n_press,
+                             gdouble          x,
+                             gdouble          y,
+                             GcalWeekGrid    *self)
 {
-  GcalWeekGrid *self = GCAL_WEEK_GRID (object);
+  GtkAllocation alloc;
+  gdouble minute_height;
+  gint column_width;
+  gint column;
+  gint minute;
 
-  g_clear_pointer (&self->events, gcal_range_tree_unref);
-  gcal_clear_date_time (&self->active_date);
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+  minute_height = (gdouble) alloc.height / MINUTES_PER_DAY;
+  column_width = floor (alloc.width / 7);
+  column = (gint) x / column_width;
+  minute = y / minute_height;
+  minute = minute - (minute % 30);
 
-  G_OBJECT_CLASS (gcal_week_grid_parent_class)->finalize (object);
+  self->selection_start = (column * MINUTES_PER_DAY + minute) / 30;
+  self->selection_end = self->selection_start;
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+
+  gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_BUBBLE);
 }
 
 static void
-gcal_week_grid_add (GtkContainer *container,
-                    GtkWidget    *widget)
+on_event_widget_activated_cb (GcalEventWidget *widget,
+                              GcalWeekGrid    *self)
 {
-  if (!gtk_widget_get_parent (widget))
-    gtk_widget_set_parent (widget, GTK_WIDGET (container));
+  g_signal_emit (self, signals[EVENT_ACTIVATED], 0, widget);
 }
 
 static void
-gcal_week_grid_remove (GtkContainer *container,
-                       GtkWidget    *widget)
+on_motion_controller_motion_cb (GtkEventControllerMotion *motion_controller,
+                                gdouble                   x,
+                                gdouble                   y,
+                                GcalWeekGrid             *self)
 {
+  GtkAllocation alloc;
+  gdouble minute_height;
+  gint column;
+  gint minute;
+
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+  minute_height = (gdouble) alloc.height / MINUTES_PER_DAY;
+  column = self->selection_start * 30 / MINUTES_PER_DAY;
+  minute = y / minute_height;
+  minute = minute - (minute % 30);
+
+  self->selection_end = (column * MINUTES_PER_DAY + minute) / 30;
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
 }
 
 static void
-gcal_week_grid_forall (GtkContainer *container,
-                       gboolean      include_internals,
-                       GtkCallback   callback,
-                       gpointer      callback_data)
+on_click_gesture_released_cb (GtkGestureClick *click_gesture,
+                              gint             n_press,
+                              gdouble          x,
+                              gdouble          y,
+                              GcalWeekGrid    *self)
 {
-  GcalWeekGrid *self;
-  GPtrArray *widgets_data;
-  guint i;
+  g_autoptr (GDateTime) week_start = NULL;
+  g_autoptr (GDateTime) start = NULL;
+  g_autoptr (GDateTime) end = NULL;
+  GtkAllocation alloc;
+  GtkWidget *weekview;
+  gboolean ltr;
+  gdouble minute_height;
+  gdouble local_x;
+  gdouble local_y;
+  gdouble out_x;
+  gdouble out_y;
+  gint column;
+  gint minute;
+  gint start_cell;
+  gint end_cell;
 
-  self = GCAL_WEEK_GRID (container);
-  widgets_data = gcal_range_tree_get_all_data (self->events);
+  ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL;
+
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+  minute_height = (gdouble) alloc.height / MINUTES_PER_DAY;
+  column = self->selection_start * 30 / MINUTES_PER_DAY;
+  minute = y / minute_height;
+  minute = minute - (minute % 30);
+
+  self->selection_end = (column * MINUTES_PER_DAY + minute) / 30;
 
-  for (i = 0; i < widgets_data->len; i++)
+  start_cell = self->selection_start;
+  end_cell = self->selection_end;
+
+  if (start_cell > end_cell)
     {
-      ChildData *data;
+      start_cell = start_cell + end_cell;
+      end_cell = start_cell - end_cell;
+      start_cell = start_cell - end_cell;
+    }
 
-      data = g_ptr_array_index (widgets_data, i);
-      callback (data->widget, callback_data);
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+
+  /* Fake the week view's event so we can control the X and Y values */
+  weekview = gtk_widget_get_ancestor (GTK_WIDGET (self), GCAL_TYPE_WEEK_VIEW);
+  week_start = gcal_date_time_get_start_of_week (self->active_date);
+
+  if (ltr)
+    {
+      start = g_date_time_add_minutes (week_start, start_cell * 30);
+      end = g_date_time_add_minutes (week_start, (end_cell + 1) * 30);
     }
+  else
+    {
+      guint rtl_start_cell, rtl_end_cell, rtl_column;
 
-  g_clear_pointer (&widgets_data, g_ptr_array_unref);
-}
+      /* Fix the minute */
+      rtl_column = 6 - column;
+      rtl_start_cell = start_cell + (rtl_column - column) * 48;
+      rtl_end_cell = (rtl_column * MINUTES_PER_DAY + minute) / 30;
 
-static void
-gcal_week_grid_get_property (GObject    *object,
-                             guint       prop_id,
-                             GValue     *value,
-                             GParamSpec *pspec)
-{
-  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-}
+      start = g_date_time_add_minutes (week_start, rtl_start_cell * 30);
+      end = g_date_time_add_minutes (week_start, (rtl_end_cell + 1) * 30);
+    }
 
-static void
-gcal_week_grid_set_property (GObject      *object,
-                             guint         prop_id,
-                             const GValue *value,
-                             GParamSpec   *pspec)
-{
-  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-}
+  local_x = round ((column + 0.5) * (alloc.width / 7.0));
+  local_y = (minute + 15) * minute_height;
 
-static void
-gcal_week_grid_realize (GtkWidget *widget)
-{
-  GcalWeekGrid *self;
-  GdkWindow *parent_window;
-  GdkWindowAttr attributes;
-  gint attributes_mask;
-  GtkAllocation allocation;
+  gtk_widget_translate_coordinates (GTK_WIDGET (self),
+                                    weekview,
+                                    local_x,
+                                    local_y,
+                                    &out_x,
+                                    &out_y);
 
-  self = GCAL_WEEK_GRID (widget);
-  parent_window = gtk_widget_get_parent_window (widget);
-
-  gtk_widget_set_realized (widget, TRUE);
-  gtk_widget_set_window (widget, parent_window);
-  g_object_ref (parent_window);
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  attributes.window_type = GDK_WINDOW_CHILD;
-  attributes.wclass = GDK_INPUT_ONLY;
-  attributes.x = allocation.x;
-  attributes.y = allocation.y;
-  attributes.width = allocation.width;
-  attributes.height = allocation.height;
-  attributes.event_mask = gtk_widget_get_events (widget);
-  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
-                            GDK_BUTTON_RELEASE_MASK |
-                            GDK_BUTTON1_MOTION_MASK |
-                            GDK_POINTER_MOTION_HINT_MASK |
-                            GDK_POINTER_MOTION_MASK |
-                            GDK_ENTER_NOTIFY_MASK |
-                            GDK_LEAVE_NOTIFY_MASK |
-                            GDK_SCROLL_MASK |
-                            GDK_SMOOTH_SCROLL_MASK);
-  attributes_mask = GDK_WA_X | GDK_WA_Y;
-
-  self->event_window = gdk_window_new (parent_window,
-                                       &attributes,
-                                       attributes_mask);
-  gtk_widget_register_window (widget, self->event_window);
+  g_signal_emit_by_name (weekview,
+                         "create-event",
+                         start,
+                         end,
+                         out_x,
+                         out_y);
+
+  gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE);
 }
 
 static void
-gcal_week_grid_unrealize (GtkWidget *widget)
+gcal_week_grid_dispose (GObject *object)
 {
-  GcalWeekGrid *self;
-
-  self = GCAL_WEEK_GRID (widget);
+  GcalWeekGrid *self = GCAL_WEEK_GRID (object);
 
-  if (self->event_window)
-    {
-      gtk_widget_unregister_window (widget, self->event_window);
-      gdk_window_destroy (self->event_window);
-      self->event_window = NULL;
-    }
+  g_clear_pointer (&self->events, gcal_range_tree_unref);
+  g_clear_pointer (&self->now_strip, gtk_widget_unparent);
 
-  GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->unrealize (widget);
+  G_OBJECT_CLASS (gcal_week_grid_parent_class)->dispose (object);
 }
 
 static void
-gcal_week_grid_map (GtkWidget *widget)
+gcal_week_grid_finalize (GObject *object)
 {
-  GcalWeekGrid *self;
-
-  self = GCAL_WEEK_GRID (widget);
+  GcalWeekGrid *self = GCAL_WEEK_GRID (object);
 
-  if (self->event_window)
-    gdk_window_show (self->event_window);
+  gcal_clear_date_time (&self->active_date);
 
-  GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->map (widget);
+  G_OBJECT_CLASS (gcal_week_grid_parent_class)->finalize (object);
 }
 
 static void
-gcal_week_grid_unmap (GtkWidget *widget)
+gcal_week_grid_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
 {
-  GcalWeekGrid *self;
-
-  self = GCAL_WEEK_GRID (widget);
-
-  if (self->event_window)
-    gdk_window_hide (self->event_window);
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
 
-  GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->unmap (widget);
+static void
+gcal_week_grid_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
 static inline gint
@@ -357,40 +379,95 @@ get_today_column (GcalWeekGrid *self)
   return days_diff;
 }
 
-static gboolean
-gcal_week_grid_draw (GtkWidget *widget,
-                     cairo_t   *cr)
+static void
+gcal_week_grid_measure (GtkWidget      *widget,
+                        GtkOrientation  orientation,
+                        gint            for_size,
+                        gint           *minimum,
+                        gint           *natural,
+                        gint           *minimum_baseline,
+                        gint           *natural_baseline)
+{
+  g_autoptr (PangoLayout) layout = NULL;
+  PangoFontDescription *font_desc;
+  GtkStyleContext *context;
+  PangoContext *pango_context;
+  GtkBorder padding;
+  gint hours_12_height;
+  gint hours_24_height;
+  gint cell_height;
+  gint height;
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      if (minimum)
+        *minimum = 1;
+      if (natural)
+        *natural = 1;
+      return;
+    }
+
+  context = gtk_widget_get_style_context (widget);
+
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, "hours");
+
+  gtk_style_context_get_padding (context, &padding);
+
+  pango_context = gtk_widget_get_pango_context (widget);
+  font_desc = pango_context_get_font_description (pango_context);
+
+  layout = pango_layout_new (pango_context);
+  pango_layout_set_font_description (layout, font_desc);
+
+  pango_layout_set_text (layout, _("00 AM"), -1);
+  pango_layout_get_pixel_size (layout, NULL, &hours_12_height);
+
+  pango_layout_set_text (layout, _("00:00"), -1);
+  pango_layout_get_pixel_size (layout, NULL, &hours_24_height);
+
+  cell_height = MAX (hours_12_height, hours_24_height) + padding.top + padding.bottom;
+  height = cell_height * 48;
+
+  gtk_style_context_restore (context);
+
+  /* Report the height */
+  if (minimum)
+    *minimum = height;
+
+  if (natural)
+    *natural = height;
+}
+
+static void
+gcal_week_grid_snapshot (GtkWidget   *widget,
+                         GtkSnapshot *snapshot)
 {
+  g_autoptr (GPtrArray) widgets = NULL;
   GtkStyleContext *context;
   GtkStateFlags state;
   GcalWeekGrid *self;
   GtkBorder padding;
   GdkRGBA color;
-
-  gboolean ltr;
   gdouble minutes_height;
   gdouble x, column_width;
-  gint i, width, height, today_column;
+  guint i;
+  gint width, height;
 
   self = GCAL_WEEK_GRID (widget);
   context = gtk_widget_get_style_context (widget);
   state = gtk_widget_get_state_flags (widget);
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
 
   gtk_style_context_save (context);
   gtk_style_context_add_class (context, "lines");
-  gtk_style_context_get_color (context, state, &color);
-  gtk_style_context_get_padding (context, state, &padding);
+  gtk_style_context_get_color (context, &color);
+  gtk_style_context_get_padding (context, &padding);
 
-  gdk_cairo_set_source_rgba (cr, &color);
-
-  width = gtk_widget_get_allocated_width (widget);
-  height = gtk_widget_get_allocated_height (widget);
+  width = gtk_widget_get_width (widget);
+  height = gtk_widget_get_height (widget);
   column_width = width / 7.0;
   minutes_height = (gdouble) height / MINUTES_PER_DAY;
 
-  cairo_set_line_width (cr, 0.65);
-
   /* First, draw the selection */
   if (self->selection_start != -1 && self->selection_end != -1)
     {
@@ -418,12 +495,12 @@ gcal_week_grid_draw (GtkWidget *widget,
       gtk_style_context_save (context);
       gtk_style_context_set_state (context, state | GTK_STATE_FLAG_SELECTED);
 
-      gtk_render_background (context,
-                             cr,
-                             ALIGNED (x),
-                             round ((start * 30 % MINUTES_PER_DAY) * minutes_height),
-                             column_width,
-                             selection_height);
+      gtk_snapshot_render_background (snapshot,
+                                      context,
+                                      ALIGNED (x),
+                                      round ((start * 30 % MINUTES_PER_DAY) * minutes_height),
+                                      column_width,
+                                      selection_height);
 
       gtk_style_context_restore (context);
     }
@@ -438,93 +515,35 @@ gcal_week_grid_draw (GtkWidget *widget,
       column = self->dnd_cell / (MINUTES_PER_DAY / 30);
       row = self->dnd_cell - column * 48;
 
-      gtk_render_background (context,
-                             cr,
-                             column * column_width,
-                             row * cell_height,
-                             column_width,
-                             cell_height);
-    }
-
-  /* Vertical lines */
-  for (i = 0; i < 7; i++)
-    {
-      if (ltr)
-        x = column_width * i;
-      else
-        x = width - column_width * i;
-
-      cairo_move_to (cr, ALIGNED (x), 0);
-      cairo_rel_line_to (cr, 0, height);
+      gtk_snapshot_render_background (snapshot,
+                                      context,
+                                      column * column_width,
+                                      row * cell_height,
+                                      column_width,
+                                      cell_height);
     }
 
-  /* Horizontal lines */
-  for (i = 1; i < 24; i++)
-    {
-      cairo_move_to (cr, 0, ALIGNED ((height / 24.0) * i));
-      cairo_rel_line_to (cr, width, 0);
-    }
-
-  cairo_stroke (cr);
-
-  /* Dashed lines between the vertical lines */
-  cairo_set_dash (cr, dashed, 2, 0);
-
-  for (i = 0; i < 24; i++)
-    {
-      cairo_move_to (cr, 0, ALIGNED ((height / 24.0) * i + (height / 48.0)));
-      cairo_rel_line_to (cr, width, 0);
-    }
-
-  cairo_stroke (cr);
+  gcal_week_view_common_snapshot_hour_lines (widget, snapshot, GTK_ORIENTATION_HORIZONTAL, &color, width, 
height);
+  gcal_week_view_common_snapshot_hour_lines (widget, snapshot, GTK_ORIENTATION_VERTICAL, &color, width, 
height);
 
   gtk_style_context_restore (context);
 
-  GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->draw (widget, cr);
-
-  /* Today column */
-  today_column = get_today_column (GCAL_WEEK_GRID (widget));
-
-  if (today_column != -1)
+  widgets = gcal_range_tree_get_all_data (self->events);
+  for (i = 0; i < widgets->len; i++)
     {
-      g_autoptr (GDateTime) now = NULL;
-      GtkBorder margin;
-      gdouble strip_width;
-      guint minutes_from_midnight;
-      gint min_stip_height;
-
-      now = g_date_time_new_now_local ();
-      minutes_from_midnight = g_date_time_get_hour (now) * 60 + g_date_time_get_minute (now);
-
-      gtk_style_context_save (context);
-      gtk_style_context_add_class (context, "now-strip");
-
-      gtk_style_context_get (context, state, "min-height", &min_stip_height, NULL);
-      gtk_style_context_get_margin (context, state, &margin);
-
-      strip_width = column_width - margin.left - margin.right;
-
-      if (ltr)
-        x = today_column * column_width + margin.left;
-      else
-        x = width - (today_column * column_width + margin.right) - strip_width;
-
-      gtk_render_background (context,
-                             cr,
-                             x,
-                             round (minutes_from_midnight * ((gdouble) height / MINUTES_PER_DAY) + 
margin.top),
-                             strip_width,
-                             MAX (1, min_stip_height - margin.top - margin.bottom));
+      ChildData *child_data = g_ptr_array_index (widgets, i);
 
-      gtk_style_context_restore (context);
+      gtk_widget_snapshot_child (widget, child_data->widget, snapshot);
     }
 
-  return FALSE;
+  gtk_widget_snapshot_child (widget, self->now_strip, snapshot);
 }
 
 static void
-gcal_week_grid_size_allocate (GtkWidget     *widget,
-                              GtkAllocation *allocation)
+gcal_week_grid_size_allocate (GtkWidget *widget,
+                              gint       width,
+                              gint       height,
+                              gint       baseline)
 {
   GcalWeekGrid *self = GCAL_WEEK_GRID (widget);
   g_autoptr (GDateTime) week_start = NULL;
@@ -534,25 +553,13 @@ gcal_week_grid_size_allocate (GtkWidget     *widget,
   gdouble column_width;
   guint x, y;
   guint i;
-
-  /* Allocate the widget */
-  gtk_widget_set_allocation (widget, allocation);
+  gint today_column;
 
   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
 
-  if (gtk_widget_get_realized (widget))
-    {
-      gdk_window_move_resize (self->event_window,
-                              allocation->x,
-                              allocation->y,
-                              allocation->width,
-                              allocation->height);
-    }
-
-
   /* Preliminary calculations */
-  minutes_height = (gdouble) allocation->height / MINUTES_PER_DAY;
-  column_width = (gdouble) allocation->width / 7.0;
+  minutes_height = (gdouble) height / MINUTES_PER_DAY;
+  column_width = (gdouble) width / 7.0;
 
   /* Temporary range tree to hold positioned events' indexes */
   overlaps = gcal_range_tree_new ();
@@ -578,26 +585,27 @@ gcal_week_grid_size_allocate (GtkWidget     *widget,
         {
           g_autoptr (GDateTime) event_start = NULL;
           g_autoptr (GDateTime) event_end = NULL;
-          GtkStyleContext *context;
           GtkAllocation child_allocation;
           GtkWidget *event_widget;
           GcalRange *event_range;
           ChildData *data;
-          GtkBorder margin;
           guint64 events_at_range;
           gint event_minutes;
           gint natural_height;
           gint widget_index;
           gint offset;
-          gint height;
-          gint width;
+          gint event_height;
+          gint event_width;
 
           data =  g_ptr_array_index (widgets_data, j);
           event_widget = data->widget;
+
+          if (!gtk_widget_should_layout (event_widget))
+            continue;
+
           event_range = gcal_event_get_range (data->event);
           event_start = g_date_time_to_local (gcal_event_get_date_start (data->event));
           event_end = g_date_time_to_local (gcal_event_get_date_end (data->event));
-          context = gtk_widget_get_style_context (event_widget);
 
           /* The total number of events available in this range */
           events_at_range = count_overlaps_at_range (self->events, event_range);
@@ -606,36 +614,26 @@ gcal_week_grid_size_allocate (GtkWidget     *widget,
           widget_index = get_event_index (overlaps, event_range);
 
           /* Gtk complains about that */
-          gtk_widget_get_preferred_height (event_widget, NULL, &natural_height);
-
-          /* Consider the margins of the child */
-          gtk_style_context_get_margin (context,
-                                        gtk_style_context_get_state (context),
-                                        &margin);
+          gtk_widget_measure (event_widget, GTK_ORIENTATION_VERTICAL, -1, NULL, &natural_height, NULL, NULL);
 
           event_minutes = g_date_time_difference (event_end, event_start) / G_TIME_SPAN_MINUTE;
-          width = column_width / events_at_range - margin.left - margin.right;
-          height = event_minutes * minutes_height - margin.top - margin.bottom;
-          offset = (width + margin.left + margin.right) * widget_index;
-          y = (g_date_time_get_hour (event_start) * 60 + g_date_time_get_minute (event_start)) * 
minutes_height + margin.top;
+          event_width = column_width / events_at_range;
+          event_height = event_minutes * minutes_height;
+          offset = event_width * widget_index;
+          y = (g_date_time_get_hour (event_start) * 60 + g_date_time_get_minute (event_start)) * 
minutes_height;
 
           if (ltr)
-            x = column_width * i + offset + allocation->x + margin.left + 1;
+            x = column_width * i + offset + 1;
           else
-            x = allocation->width - width - (column_width * i + offset + allocation->x + margin.left + 1);
-
-          /* TODO: find a better way to handle line widths */
-          height -= 2;
-          width -= 1;
-          y += 2;
+            x = width - event_width - (column_width * i + offset + x + 1);
 
           /* Setup the child position and size */
           child_allocation.x = x;
           child_allocation.y = y;
-          child_allocation.width = width;
-          child_allocation.height = height;
+          child_allocation.width = event_width;
+          child_allocation.height = event_height;
 
-          gtk_widget_size_allocate (event_widget, &child_allocation);
+          gtk_widget_size_allocate (event_widget, &child_allocation, baseline);
 
           /*
            * Add the current event to the temporary overlaps tree so we have a way to
@@ -648,202 +646,45 @@ gcal_week_grid_size_allocate (GtkWidget     *widget,
     }
 
   g_clear_pointer (&overlaps, gcal_range_tree_unref);
-}
-
-static void
-gcal_week_grid_get_preferred_height (GtkWidget *widget,
-                                     gint      *minimum_height,
-                                     gint      *natural_height)
-{
-  GtkStyleContext *context;
-  GtkStateFlags state;
-  GtkBorder padding;
-
-  PangoLayout *layout;
-  PangoFontDescription *font_desc;
-
-  gint hours_12_height, hours_24_height, cell_height, height;
-
-  context = gtk_widget_get_style_context (widget);
-  state = gtk_style_context_get_state (context);
-
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, "hours");
-
-  gtk_style_context_get (context, state,
-                         "font", &font_desc,
-                         NULL);
-  gtk_style_context_get_padding (context, state, &padding);
-
-  layout = pango_layout_new (gtk_widget_get_pango_context (widget));
-  pango_layout_set_font_description (layout, font_desc);
-
-  pango_layout_set_text (layout, _("00 AM"), -1);
-  pango_layout_get_pixel_size (layout, NULL, &hours_12_height);
-
-  pango_layout_set_text (layout, _("00:00"), -1);
-  pango_layout_get_pixel_size (layout, NULL, &hours_24_height);
-
-  cell_height = MAX (hours_12_height, hours_24_height) + padding.top + padding.bottom;
-  height = cell_height * 48;
-
-  gtk_style_context_restore (context);
-
-  pango_font_description_free (font_desc);
-  g_object_unref (layout);
-
-  /* Report the height */
-  if (minimum_height)
-    *minimum_height = height;
-
-  if (natural_height)
-    *natural_height = height;
-}
-
-static gboolean
-gcal_week_grid_button_press (GtkWidget      *widget,
-                             GdkEventButton *event_button)
-{
-  GcalWeekGrid *self;
-  GtkAllocation alloc;
-  gdouble minute_height;
-  gint column_width;
-  gint column;
-  gint minute;
-
-  self = GCAL_WEEK_GRID (widget);
-
-  gtk_widget_get_allocation (widget, &alloc);
-  minute_height = (gdouble) alloc.height / MINUTES_PER_DAY;
-  column_width = floor (alloc.width / 7);
-  column = (gint) event_button->x / column_width;
-  minute = event_button->y / minute_height;
-  minute = minute - (minute % 30);
-
-  self->selection_start = (column * MINUTES_PER_DAY + minute) / 30;
-  self->selection_end = self->selection_start;
-
-  gtk_widget_queue_draw (widget);
-
-  return GDK_EVENT_PROPAGATE;
-}
-
-static gboolean
-gcal_week_grid_motion_notify_event (GtkWidget      *widget,
-                                    GdkEventMotion *event)
-{
-  GcalWeekGrid *self;
-  GtkAllocation alloc;
-  gdouble minute_height;
-  gint column;
-  gint minute;
-
-  if (!(event->state & GDK_BUTTON_PRESS_MASK))
-    return GDK_EVENT_PROPAGATE;
-
-  self = GCAL_WEEK_GRID (widget);
-
-  gtk_widget_get_allocation (widget, &alloc);
-  minute_height = (gdouble) alloc.height / MINUTES_PER_DAY;
-  column = self->selection_start * 30 / MINUTES_PER_DAY;
-  minute = event->y / minute_height;
-  minute = minute - (minute % 30);
-
-  self->selection_end = (column * MINUTES_PER_DAY + minute) / 30;
-
-  gtk_widget_queue_draw (widget);
-
-  return GDK_EVENT_STOP;
-}
-
-static gboolean
-gcal_week_grid_button_release (GtkWidget      *widget,
-                               GdkEventButton *event)
-{
-  GcalWeekGrid *self;
-  GtkAllocation alloc;
-  GDateTime *week_start;
-  GDateTime *start, *end;
-  GtkWidget *weekview;
-  gboolean ltr;
-  gdouble minute_height;
-  gdouble x, y;
-  gint column;
-  gint minute;
-  gint start_cell;
-  gint end_cell;
-  gint out_x;
-  gint out_y;
 
-  self = GCAL_WEEK_GRID (widget);
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-
-  gtk_widget_get_allocation (widget, &alloc);
-  minute_height = (gdouble) alloc.height / MINUTES_PER_DAY;
-  column = self->selection_start * 30 / MINUTES_PER_DAY;
-  minute = event->y / minute_height;
-  minute = minute - (minute % 30);
-
-  self->selection_end = (column * MINUTES_PER_DAY + minute) / 30;
+  /* Today column */
+  today_column = get_today_column (GCAL_WEEK_GRID (widget));
 
-  start_cell = self->selection_start;
-  end_cell = self->selection_end;
+  gtk_widget_set_child_visible (self->now_strip, today_column != -1);
 
-  if (start_cell > end_cell)
+  if (today_column != -1)
     {
-      start_cell = start_cell + end_cell;
-      end_cell = start_cell - end_cell;
-      start_cell = start_cell - end_cell;
-    }
+      g_autoptr (GDateTime) now = NULL;
+      GtkAllocation allocation;
+      guint minutes_from_midnight;
+      gint now_strip_height;
 
-  gtk_widget_queue_draw (widget);
+      now = g_date_time_new_now_local ();
+      minutes_from_midnight = g_date_time_get_hour (now) * 60 + g_date_time_get_minute (now);
 
-  /* Fake the week view's event so we can control the X and Y values */
-  weekview = gtk_widget_get_ancestor (widget, GCAL_TYPE_WEEK_VIEW);
-  week_start = gcal_date_time_get_start_of_week (self->active_date);
+      gtk_widget_measure (self->now_strip,
+                          GTK_ORIENTATION_VERTICAL,
+                          -1,
+                          &now_strip_height,
+                          NULL,
+                          NULL,
+                          NULL);
 
-  if (ltr)
-    {
-      start = g_date_time_add_minutes (week_start, start_cell * 30);
-      end = g_date_time_add_minutes (week_start, (end_cell + 1) * 30);
-    }
-  else
-    {
-      guint rtl_start_cell, rtl_end_cell, rtl_column;
+      if (ltr)
+        x = today_column * column_width;
+      else
+        x = width - (today_column * column_width) - column_width;
 
-      /* Fix the minute */
-      rtl_column = 6 - column;
-      rtl_start_cell = start_cell + (rtl_column - column) * 48;
-      rtl_end_cell = (rtl_column * MINUTES_PER_DAY + minute) / 30;
+      allocation.x = x;
+      allocation.y = round (minutes_from_midnight * ((gdouble) height / MINUTES_PER_DAY));
+      allocation.width = column_width;
+      allocation.height = MAX (1, now_strip_height);
 
-      start = g_date_time_add_minutes (week_start, rtl_start_cell * 30);
-      end = g_date_time_add_minutes (week_start, (rtl_end_cell + 1) * 30);
+      gtk_widget_size_allocate (self->now_strip, &allocation, baseline);
     }
-
-  x = round ((column + 0.5) * (alloc.width / 7.0));
-  y = (minute + 15) * minute_height;
-
-  gtk_widget_translate_coordinates (widget,
-                                    weekview,
-                                    x,
-                                    y,
-                                    &out_x,
-                                    &out_y);
-
-  g_signal_emit_by_name (weekview,
-                         "create-event",
-                         start,
-                         end,
-                         (gdouble) out_x,
-                         (gdouble) out_y);
-
-  gcal_clear_date_time (&week_start);
-  gcal_clear_date_time (&start);
-  gcal_clear_date_time (&end);
-
-  return GDK_EVENT_STOP;
 }
 
+#if 0 // TODO: DND
 static gint
 get_dnd_cell (GtkWidget *widget,
               gint       x,
@@ -995,35 +836,22 @@ gcal_week_grid_drag_leave (GtkWidget      *widget,
 
   gtk_widget_queue_draw (widget);
 }
+#endif
 
 static void
 gcal_week_grid_class_init (GcalWeekGridClass *klass)
 {
-  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  container_class->add = gcal_week_grid_add;
-  container_class->remove = gcal_week_grid_remove;
-  container_class->forall = gcal_week_grid_forall;
-
+  object_class->dispose = gcal_week_grid_dispose;
   object_class->finalize = gcal_week_grid_finalize;
   object_class->get_property = gcal_week_grid_get_property;
   object_class->set_property = gcal_week_grid_set_property;
 
-  widget_class->draw = gcal_week_grid_draw;
+  widget_class->measure = gcal_week_grid_measure;
   widget_class->size_allocate = gcal_week_grid_size_allocate;
-  widget_class->realize = gcal_week_grid_realize;
-  widget_class->unrealize = gcal_week_grid_unrealize;
-  widget_class->map = gcal_week_grid_map;
-  widget_class->unmap = gcal_week_grid_unmap;
-  widget_class->get_preferred_height = gcal_week_grid_get_preferred_height;
-  widget_class->button_press_event = gcal_week_grid_button_press;
-  widget_class->motion_notify_event = gcal_week_grid_motion_notify_event;
-  widget_class->button_release_event = gcal_week_grid_button_release;
-  widget_class->drag_motion = gcal_week_grid_drag_motion;
-  widget_class->drag_leave = gcal_week_grid_drag_leave;
-  widget_class->drag_drop = gcal_week_grid_drag_drop;
+  widget_class->snapshot = gcal_week_grid_snapshot;
 
   signals[EVENT_ACTIVATED] = g_signal_new ("event-activated",
                                            GCAL_TYPE_WEEK_GRID,
@@ -1039,20 +867,38 @@ gcal_week_grid_class_init (GcalWeekGridClass *klass)
 static void
 gcal_week_grid_init (GcalWeekGrid *self)
 {
-  gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
+  GtkGesture *click_gesture;
 
   self->selection_start = -1;
   self->selection_end = -1;
   self->dnd_cell = -1;
 
-  self->events = gcal_range_tree_new ();
+  self->events = gcal_range_tree_new_with_free_func (child_data_free);
+
+  self->now_strip = adw_bin_new ();
+  gtk_widget_add_css_class (self->now_strip, "now-strip");
+  gtk_widget_set_can_target (self->now_strip, FALSE);
+  gtk_widget_set_parent (self->now_strip, GTK_WIDGET (self));
 
+  click_gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (click_gesture), GDK_BUTTON_PRIMARY);
+  g_signal_connect (click_gesture, "pressed", G_CALLBACK (on_click_gesture_pressed_cb), self);
+  g_signal_connect (click_gesture, "released", G_CALLBACK (on_click_gesture_released_cb), self);
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (click_gesture));
+
+  self->motion_controller = gtk_event_controller_motion_new ();
+  g_signal_connect (self->motion_controller, "motion", G_CALLBACK (on_motion_controller_motion_cb), self);
+  gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE);
+  gtk_widget_add_controller (GTK_WIDGET (self), self->motion_controller);
+
+#if 0 // TODO: DND
   /* Setup the week view as a drag n' drop destination */
   gtk_drag_dest_set (GTK_WIDGET (self),
                      0,
                      NULL,
                      0,
                      GDK_ACTION_MOVE);
+#endif
 }
 
 /* Public API */
@@ -1066,8 +912,8 @@ gcal_week_grid_set_context (GcalWeekGrid *self,
 
   g_signal_connect_object (gcal_context_get_clock (context),
                            "minute-changed",
-                           G_CALLBACK (gtk_widget_queue_draw),
-                           self,
+                           G_CALLBACK (gtk_widget_queue_resize),
+                           self->now_strip,
                            G_CONNECT_SWAPPED);
 }
 
@@ -1091,10 +937,9 @@ gcal_week_grid_add_event (GcalWeekGrid *self,
                              gcal_event_get_range (event),
                              child_data_new (widget, event));
 
-  setup_event_widget (self, widget);
-  gtk_widget_show (widget);
+  g_signal_connect (widget, "activate", G_CALLBACK (on_event_widget_activated_cb), self);
 
-  gtk_container_add (GTK_CONTAINER (self), widget);
+  gtk_widget_set_parent (widget, GTK_WIDGET (self));
 }
 
 void
@@ -1120,10 +965,7 @@ gcal_week_grid_remove_event (GcalWeekGrid *self,
         continue;
 
       gcal_range_tree_remove_range (self->events, gcal_event_get_range (event), data);
-      destroy_event_widget (self, data->widget);
       gtk_widget_queue_allocate (GTK_WIDGET (self));
-      g_object_unref (event);
-      g_free (data);
     }
 
   g_clear_pointer (&widgets, g_ptr_array_unref);
@@ -1134,29 +976,13 @@ gcal_week_grid_get_children_by_uuid (GcalWeekGrid          *self,
                                      GcalRecurrenceModType  mod,
                                      const gchar           *uid)
 {
-  GPtrArray *widgets;
-  GList *events;
-  GList *result;
-  guint i;
+  g_autoptr (GList) result = NULL;
 
   GCAL_ENTRY;
 
-  events = NULL;
-  result = NULL;
-  widgets = gcal_range_tree_get_all_data (self->events);
-
-  for (i = 0; widgets && i < widgets->len; i++)
-    {
-      ChildData *data = g_ptr_array_index (widgets, i);
-      events = g_list_prepend (events, data->widget);
-    }
-
-  result = filter_event_list_by_uid_and_modtype (events, mod, uid);
-
-  g_clear_pointer (&widgets, g_ptr_array_unref);
-  g_clear_pointer (&events, g_list_free);
+  result = filter_children_by_uid_and_modtype (GTK_WIDGET (self), mod, uid);
 
-  GCAL_RETURN (result);
+  GCAL_RETURN (g_steal_pointer (&result));
 }
 
 void
diff --git a/src/gui/views/gcal-week-grid.h b/src/gui/views/gcal-week-grid.h
index f21487c0..299ebe4d 100644
--- a/src/gui/views/gcal-week-grid.h
+++ b/src/gui/views/gcal-week-grid.h
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
 
 #define GCAL_TYPE_WEEK_GRID (gcal_week_grid_get_type())
 
-G_DECLARE_FINAL_TYPE (GcalWeekGrid, gcal_week_grid, GCAL, WEEK_GRID, GtkContainer)
+G_DECLARE_FINAL_TYPE (GcalWeekGrid, gcal_week_grid, GCAL, WEEK_GRID, GtkWidget)
 
 void                 gcal_week_grid_set_context                  (GcalWeekGrid       *week_grid,
                                                                   GcalContext        *context);
diff --git a/src/gui/views/gcal-week-header.c b/src/gui/views/gcal-week-header.c
index e722bdb6..3ccc8105 100644
--- a/src/gui/views/gcal-week-header.c
+++ b/src/gui/views/gcal-week-header.c
@@ -30,13 +30,12 @@
 #include "gcal-view.h"
 #include "gcal-week-header.h"
 #include "gcal-week-view.h"
+#include "gcal-week-view-common.h"
 
 #include <glib/gi18n.h>
 #include <string.h>
 #include <math.h>
 
-#define COLUMN_PADDING       6
-
 /* WeatherInfoDay:
  * @winfo: (nullable): Holds weather information for this week-day. All other fields are only valid if this 
one is not %NULL.
  * @icon_buf: (nullable): Buffered weather icon.
@@ -52,22 +51,29 @@
 typedef struct
 {
   GcalWeatherInfo    *winfo;    /* owned */
-  GdkPixbuf          *icon_buf; /* owned */
+  GtkIconPaintable   *icon_buf; /* owned */
 } WeatherInfoDay;
 
+typedef struct
+{
+  GtkWidget          *day_number_label;
+  GtkWidget          *weekday_name_label;
+} WeekdayHeader;
+
 struct _GcalWeekHeader
 {
-  GtkGrid             parent;
+  GtkBox              parent;
 
-  GtkWidget          *grid;
+  GtkGrid            *grid;
   GtkWidget          *month_label;
   GtkWidget          *week_label;
   GtkWidget          *year_label;
   GtkWidget          *scrolledwindow;
-  GtkWidget          *expand_button;
+  GtkButton          *expand_button;
   GtkWidget          *expand_button_box;
-  GtkWidget          *expand_button_image;
   GtkWidget          *header_labels_box;
+  GtkEventController *motion_controller;
+  GtkBox             *weekdays_box;
 
   GcalContext        *context;
 
@@ -78,6 +84,7 @@ struct _GcalWeekHeader
    */
   GList              *events[7];
   GtkWidget          *overflow_label[7];
+  WeekdayHeader       weekday_header[7];
 
   gint                first_weekday;
 
@@ -113,7 +120,7 @@ enum
 
 static guint signals[LAST_SIGNAL] = { 0, };
 
-G_DEFINE_TYPE (GcalWeekHeader, gcal_week_header, GTK_TYPE_GRID);
+G_DEFINE_TYPE (GcalWeekHeader, gcal_week_header, GTK_TYPE_BOX);
 
 
 /* WeatherInfoDay methods */
@@ -128,11 +135,8 @@ wid_clear (WeatherInfoDay *wid)
 {
    g_return_if_fail (wid != NULL);
 
-  if (wid->winfo != NULL)
-    g_clear_object (&wid->winfo);
-
-  if (wid->icon_buf != NULL)
-    g_clear_object (&wid->icon_buf);
+  g_clear_object (&wid->winfo);
+  g_clear_object (&wid->icon_buf);
 }
 
 static gint
@@ -229,62 +233,63 @@ destroy_event_widget (GcalWeekHeader *self,
                       GtkWidget      *widget)
 {
   g_signal_handlers_disconnect_by_func (widget, on_event_widget_activated, self);
-  gtk_widget_destroy (widget);
+  gtk_grid_remove (self->grid, widget);
 }
 
 /* Auxiliary methods */
-static gboolean
-on_button_pressed (GcalWeekHeader *self,
-                   GdkEventButton *event,
-                   GtkWidget      *widget)
+static void
+on_button_pressed (GtkGestureClick *click_gesture,
+                   gint             n_press,
+                   gdouble          x,
+                   gdouble          y,
+                   GcalWeekHeader  *self)
 {
   gboolean ltr;
   gdouble column_width;
   gint column;
   gint width;
 
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-  width = gtk_widget_get_allocated_width (widget);
+  ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL;
+  width = gtk_widget_get_allocated_width (self->scrolledwindow);
   column_width = width / 7.0;
-  column = ltr ? (event->x / column_width) : (7  - event->x / column_width);
+  column = ltr ? (x / column_width) : (7  - x / column_width);
 
   self->selection_start = column;
   self->selection_end = column;
 
   gtk_widget_queue_draw (GTK_WIDGET (self));
 
-  return GDK_EVENT_PROPAGATE;
+  gtk_event_controller_set_propagation_phase (self->motion_controller,
+                                              GTK_PHASE_BUBBLE);
 }
 
-static gboolean
-on_motion_notify (GcalWeekHeader *self,
-                  GdkEventMotion *event,
-                  GtkWidget      *widget)
+static void
+on_motion_notify (GtkEventControllerMotion *motion_event,
+                  gdouble                   x,
+                  gdouble                   y,
+                  GcalWeekHeader           *self)
 {
   gboolean ltr;
   gdouble column_width;
   gint column;
   gint width;
 
-  if (!(event->state & GDK_BUTTON_PRESS_MASK))
-    return GDK_EVENT_PROPAGATE;
-
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-  width = gtk_widget_get_allocated_width (widget);
+  ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL;
+  width = gtk_widget_get_allocated_width (self->scrolledwindow);
   column_width = width / 7.0;
-  column = ltr ? (event->x / column_width) : (7  - event->x / column_width);
+  column = ltr ? (x / column_width) : (7  - x / column_width);
 
   self->selection_end = column;
 
   gtk_widget_queue_draw (GTK_WIDGET (self));
-
-  return GDK_EVENT_STOP;
 }
 
-static gboolean
-on_button_released (GcalWeekHeader *self,
-                    GdkEventButton *event,
-                    GtkWidget      *widget)
+static void
+on_button_released (GtkGestureClick *click_gesture,
+                    gint             n_press,
+                    gdouble          x,
+                    gdouble          y,
+                    GcalWeekHeader  *self)
 {
   g_autoptr (GDateTime) selection_start = NULL;
   g_autoptr (GDateTime) selection_end = NULL;
@@ -292,23 +297,23 @@ on_button_released (GcalWeekHeader *self,
   GtkWidget *weekview;
   gboolean ltr;
   gdouble column_width;
-  gint out_x, out_y;
+  gdouble out_x, out_y;
   gint column;
   gint width;
   gint start;
   gint end;
 
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-  width = gtk_widget_get_allocated_width (widget);
+  ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL;
+  width = gtk_widget_get_allocated_width (self->scrolledwindow);
   column_width = width / 7.0;
-  column = ltr ? (event->x / column_width) : (7  - event->x / column_width);
+  column = ltr ? (x / column_width) : (7  - x / column_width);
 
   self->selection_end = column;
 
   gtk_widget_queue_draw (GTK_WIDGET (self));
 
   /* Fake the week view's event so we can control the X and Y values */
-  weekview = gtk_widget_get_ancestor (widget, GCAL_TYPE_WEEK_VIEW);
+  weekview = gtk_widget_get_ancestor (GTK_WIDGET (self), GCAL_TYPE_WEEK_VIEW);
 
   start = self->selection_start;
   end = self->selection_end;
@@ -327,7 +332,7 @@ on_button_released (GcalWeekHeader *self,
   out_x = ltr ? (column_width * (column + 0.5)) : (width - column_width * (column + 0.5));
 
   /* Translate X... */
-  gtk_widget_translate_coordinates (widget, weekview, out_x, 0, &out_x, NULL);
+  gtk_widget_translate_coordinates (self->scrolledwindow, weekview, out_x, 0, &out_x, NULL);
 
   /* And Y */
   gtk_widget_translate_coordinates (GTK_WIDGET (self),
@@ -341,10 +346,11 @@ on_button_released (GcalWeekHeader *self,
                          "create-event",
                          selection_start,
                          selection_end,
-                         (gdouble) out_x,
-                         (gdouble) out_y);
+                         out_x,
+                         out_y);
 
-  return GDK_EVENT_STOP;
+  gtk_event_controller_set_propagation_phase (self->motion_controller,
+                                              GTK_PHASE_NONE);
 }
 
 static void
@@ -464,15 +470,11 @@ update_overflow (GcalWeekHeader *self)
 
           text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "Other event", "Other %d events", n_events - 
2), n_events - 2);
 
-          /* Show the button if not visible yet */
-          if (!gtk_widget_get_visible (self->expand_button))
-            gtk_widget_show (self->expand_button);
-
           /* TODO: use a button and show an overflow popover */
           if (!label)
             {
               label = gtk_label_new ("");
-              gtk_grid_attach (GTK_GRID (self->grid),
+              gtk_grid_attach (self->grid,
                                label,
                                i,
                                3,
@@ -480,8 +482,6 @@ update_overflow (GcalWeekHeader *self)
                                1);
 
               self->overflow_label[i] = label;
-
-              gtk_widget_show (label);
             }
 
           gtk_label_set_label (GTK_LABEL (label), text);
@@ -489,12 +489,12 @@ update_overflow (GcalWeekHeader *self)
         }
       else if (label)
         {
-          gtk_widget_destroy (label);
+          gtk_grid_remove (self->grid, label);
           self->overflow_label[i] = NULL;
         }
     }
 
-  gtk_widget_set_visible (self->expand_button, show_expand);
+  gtk_widget_set_visible (GTK_WIDGET (self->expand_button), show_expand);
 }
 
 static void
@@ -502,6 +502,8 @@ merge_events (GcalWeekHeader *self,
               GtkWidget      *event,
               GtkWidget      *to_be_removed)
 {
+  GtkLayoutManager *layout_manager;
+  GtkLayoutChild *layout_child;
   GDateTime *end_date;
   gint deleted_width, current_width;
 
@@ -510,23 +512,18 @@ merge_events (GcalWeekHeader *self,
   gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (event), end_date);
 
   /* Retrieve the current sizes */
-  gtk_container_child_get (GTK_CONTAINER (self->grid),
-                           to_be_removed,
-                           "width", &deleted_width,
-                           NULL);
+  layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid));
+  layout_child = gtk_layout_manager_get_layout_child (layout_manager, to_be_removed);
+  deleted_width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child));
 
-  gtk_container_child_get (GTK_CONTAINER (self->grid),
-                           event,
-                           "width", &current_width,
-                           NULL);
+  layout_child = gtk_layout_manager_get_layout_child (layout_manager, event);
+  current_width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child));
 
   destroy_event_widget (self, to_be_removed);
 
   /* Update the event's size */
-  gtk_container_child_set (GTK_CONTAINER (self->grid),
-                           event,
-                           "width", current_width + deleted_width,
-                           NULL);
+  gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child),
+                                         current_width + deleted_width);
 }
 
 static void
@@ -587,8 +584,8 @@ check_mergeable_events (GcalWeekHeader *self)
             {
               GtkWidget *current_widget, *to_be_removed;
 
-              current_widget = gtk_grid_get_child_at (GTK_GRID (self->grid), weekday + i, index + 1);
-              to_be_removed = gtk_grid_get_child_at (GTK_GRID (self->grid), weekday + i + 1, index + 1);
+              current_widget = gtk_grid_get_child_at (self->grid, weekday + i, index + 1);
+              to_be_removed = gtk_grid_get_child_at (self->grid, weekday + i + 1, index + 1);
 
               /*
                * We don't want to merge:
@@ -622,6 +619,8 @@ split_event_widget_at_column (GcalWeekHeader *self,
                               GtkWidget      *widget,
                               gint            column)
 {
+  GtkLayoutManager *layout_manager;
+  GtkLayoutChild *layout_child;
   GDateTime *week_start, *column_date, *end_column_date;
   gboolean create_before;
   gboolean create_after;
@@ -634,12 +633,11 @@ split_event_widget_at_column (GcalWeekHeader *self,
   column_date = g_date_time_add_days (week_start, column);
   end_column_date = g_date_time_add_days (column_date, 1);
 
-  gtk_container_child_get (GTK_CONTAINER (self->grid),
-                           widget,
-                           "top_attach", &top_attach,
-                           "left_attach", &left_attach,
-                           "width", &old_width,
-                           NULL);
+  layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid));
+  layout_child = gtk_layout_manager_get_layout_child (layout_manager, widget);
+  top_attach = gtk_grid_layout_child_get_row (GTK_GRID_LAYOUT_CHILD (layout_child));
+  left_attach = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child));
+  old_width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child));
 
   create_before = column > 0 && left_attach < column;
   create_after = column < 6 && old_width > 1 && left_attach + old_width > column + 1;
@@ -653,7 +651,7 @@ split_event_widget_at_column (GcalWeekHeader *self,
 
       setup_event_widget (self, widget_before);
 
-      gtk_grid_attach (GTK_GRID (self->grid),
+      gtk_grid_attach (self->grid,
                        widget_before,
                        left_attach,
                        top_attach,
@@ -667,11 +665,8 @@ split_event_widget_at_column (GcalWeekHeader *self,
       left_attach = column;
 
       /* Update the current event position, size and start date */
-      gtk_container_child_set (GTK_CONTAINER (self->grid),
-                               widget,
-                               "left_attach", left_attach,
-                               "width", new_width,
-                               NULL);
+      gtk_grid_layout_child_set_column (GTK_GRID_LAYOUT_CHILD (layout_child), left_attach);
+      gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child), new_width);
 
       gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (widget), column_date);
       gtk_widget_set_visible (widget, is_event_visible (self, left_attach, top_attach - 1));
@@ -691,7 +686,7 @@ split_event_widget_at_column (GcalWeekHeader *self,
 
       setup_event_widget (self, widget_after);
 
-      gtk_grid_attach (GTK_GRID (self->grid),
+      gtk_grid_attach (self->grid,
                        widget_after,
                        column + 1,
                        top_attach,
@@ -701,10 +696,7 @@ split_event_widget_at_column (GcalWeekHeader *self,
       new_width = column - left_attach + 1;
 
       /* Only update the current widget's width */
-      gtk_container_child_set (GTK_CONTAINER (self->grid),
-                               widget,
-                               "width", new_width,
-                               NULL);
+      gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child), new_width);
 
       gtk_widget_set_visible (widget_after, is_event_visible (self, column + 1, top_attach - 1));
     }
@@ -720,42 +712,39 @@ move_events_at_column (GcalWeekHeader *self,
                        gint            column,
                        gint            start_at)
 {
-  GList *children, *l;
+  GtkLayoutManager *layout_manager;
+  GtkWidget *child;
 
-  children = gtk_container_get_children (GTK_CONTAINER (self->grid));
+  layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid));
 
   /* First, lets find the widgets at this column */
-  for (l = children; l != NULL; l = l->next)
+  for (child = gtk_widget_get_first_child (GTK_WIDGET (self->grid));
+       child;
+       child = gtk_widget_get_next_sibling (child))
     {
       gint top_attach, left_attach, width;
+      GtkLayoutChild *layout_child;
 
       /* Get the widget's current position... */
-      gtk_container_child_get (GTK_CONTAINER (self->grid),
-                               l->data,
-                               "top_attach", &top_attach,
-                               "left_attach", &left_attach,
-                               "width", &width,
-                               NULL);
-
-      if (left_attach != column || start_at > top_attach - 1 || !GCAL_IS_EVENT_WIDGET (l->data))
+      layout_child = gtk_layout_manager_get_layout_child (layout_manager, child);
+      top_attach = gtk_grid_layout_child_get_row (GTK_GRID_LAYOUT_CHILD (layout_child));
+      left_attach = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child));
+      width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child));
+
+      if (left_attach != column || start_at > top_attach - 1 || !GCAL_IS_EVENT_WIDGET (child))
         continue;
 
       /* If this is a multiday event, break it */
       if (width > 1)
-        split_event_widget_at_column (self, l->data, column);
+        split_event_widget_at_column (self, child, column);
 
       top_attach = top_attach + (direction == DOWN ? 1 : -1);
 
       /* And move it to position + 1 */
-      gtk_container_child_set (GTK_CONTAINER (self->grid),
-                               l->data,
-                               "top_attach", top_attach,
-                               NULL);
+      gtk_grid_layout_child_set_row (GTK_GRID_LAYOUT_CHILD (layout_child), top_attach);
 
-      gtk_widget_set_visible (l->data, is_event_visible (self, left_attach, top_attach - 1));
+      gtk_widget_set_visible (child, is_event_visible (self, left_attach, top_attach - 1));
     }
-
-  g_clear_pointer (&children, g_list_free);
 }
 
 static void
@@ -771,7 +760,7 @@ apply_overflow_at_weekday (GcalWeekHeader *self,
   if (self->expanded || self->overflow_label[weekday] || g_list_length (self->events[weekday]) < 4)
     return;
 
-  child = gtk_grid_get_child_at (GTK_GRID (self->grid), weekday, 3);
+  child = gtk_grid_get_child_at (self->grid, weekday, 3);
 
   split_event_widget_at_column (self, child, weekday);
   gtk_widget_hide (child);
@@ -785,6 +774,7 @@ add_event_to_grid (GcalWeekHeader *self,
 {
   g_autoptr (GDateTime) week_start = NULL;
   g_autoptr (GDateTime) week_end = NULL;
+  GtkLayoutManager *layout_manager;
   GtkWidget *widget;
   gboolean is_visible, was_visible;
   gint position;
@@ -802,7 +792,7 @@ add_event_to_grid (GcalWeekHeader *self,
   widget = gcal_event_widget_new (self->context, event);
   setup_event_widget (self, widget);
 
-  gtk_grid_attach (GTK_GRID (self->grid),
+  gtk_grid_attach (self->grid,
                    widget,
                    start,
                    position + 1,
@@ -827,6 +817,8 @@ add_event_to_grid (GcalWeekHeader *self,
   gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (widget), week_start);
   gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (widget), week_end);
 
+  layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid));
+
   /*
    * In addition to moving the current column's events below, multiday
    * events must also move the events from ~all~ the columns it spans
@@ -847,10 +839,11 @@ add_event_to_grid (GcalWeekHeader *self,
       /* Add the event to the grid */
       if (new_position == position && was_visible == is_visible)
         {
-          gtk_container_child_set (GTK_CONTAINER (self->grid),
-                                   widget,
-                                   "width", i - start + 1,
-                                   NULL);
+          GtkLayoutChild *layout_child;
+
+          layout_child = gtk_layout_manager_get_layout_child (layout_manager, widget);
+          gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child),
+                                                 i - start + 1);
         }
       else
         {
@@ -861,7 +854,7 @@ add_event_to_grid (GcalWeekHeader *self,
           cloned_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (widget));
           setup_event_widget (self, cloned_widget);
 
-          gtk_grid_attach (GTK_GRID (self->grid),
+          gtk_grid_attach (self->grid,
                            cloned_widget,
                            i,
                            new_position + 1,
@@ -971,6 +964,8 @@ update_title (GcalWeekHeader *self)
 {
   GDateTime *week_start, *week_end, *week_mid;
   gchar *year_label, *month_label, *week_label;
+  gint today_column;
+  gint i;
 
   if(!self->active_date)
     return;
@@ -1007,6 +1002,44 @@ update_title (GcalWeekHeader *self)
   gtk_label_set_label (GTK_LABEL (self->week_label), week_label);
   gtk_label_set_label (GTK_LABEL (self->year_label), year_label);
 
+  today_column = get_today_column (self);
+
+  for (i = 0; i < 7; i++)
+    {
+      g_autoptr (GDateTime) day = NULL;
+      g_autofree gchar *weekday_date = NULL;
+      g_autofree gchar *weekday_abv = NULL;
+      g_autofree gchar *weekday = NULL;
+      WeekdayHeader *header;
+      gint n_day;
+
+      day = g_date_time_add_days (week_start, i);
+      n_day = g_date_time_get_day_of_month (day);
+
+      if (n_day > g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year 
(week_start)))
+        n_day = n_day - g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year 
(week_start));
+
+      header = &self->weekday_header[i];
+
+      if (i == today_column)
+        {
+          gtk_widget_add_css_class (header->weekday_name_label, "accent");
+          gtk_widget_add_css_class (header->day_number_label, "accent");
+        }
+      else
+        {
+          gtk_widget_remove_css_class (header->weekday_name_label, "accent");
+          gtk_widget_remove_css_class (header->day_number_label, "accent");
+        }
+
+      weekday_date = g_strdup_printf ("%d", n_day);
+      gtk_label_set_label (GTK_LABEL (header->day_number_label), weekday_date);
+
+      weekday = g_date_time_format (day, "%a");
+      weekday_abv = g_utf8_strup (weekday, -1);
+      gtk_label_set_label (GTK_LABEL (header->weekday_name_label), weekday_abv);
+    }
+
   g_clear_pointer (&week_start, g_date_time_unref);
   g_clear_pointer (&week_end, g_date_time_unref);
   g_clear_pointer (&week_mid, g_date_time_unref);
@@ -1018,9 +1051,8 @@ update_title (GcalWeekHeader *self)
 static void
 header_collapse (GcalWeekHeader *self)
 {
-  GList *children, *l;
-
-  children = gtk_container_get_children (GTK_CONTAINER (self->grid));
+  GtkLayoutManager *layout_manager;
+  GtkWidget *child;
 
   self->expanded = FALSE;
 
@@ -1028,36 +1060,38 @@ header_collapse (GcalWeekHeader *self)
                                   GTK_POLICY_NEVER,
                                   GTK_POLICY_NEVER);
   gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (self->scrolledwindow), -1);
-  gtk_image_set_from_icon_name (GTK_IMAGE (self->expand_button_image), "go-down-symbolic", 4);
+  gtk_button_set_icon_name (self->expand_button, "go-down-symbolic");
+
+  layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid));
 
-  for (l = children; l != NULL; l = l->next)
+  for (child = gtk_widget_get_first_child (GTK_WIDGET (self->grid));
+       child;
+       child = gtk_widget_get_next_sibling (child))
     {
+      GtkLayoutChild *layout_child;
       gint top_attach, left_attach;
 
-      gtk_container_child_get (GTK_CONTAINER (self->grid),
-                               l->data,
-                               "top-attach", &top_attach,
-                               "left_attach", &left_attach,
-                               NULL);
+      layout_child = gtk_layout_manager_get_layout_child (layout_manager, child);
+      left_attach = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child));
+      top_attach = gtk_grid_layout_child_get_row (GTK_GRID_LAYOUT_CHILD (layout_child));
 
-      gtk_widget_set_visible (l->data, is_event_visible (self, left_attach, top_attach - 1));
+      gtk_widget_set_visible (child, is_event_visible (self, left_attach, top_attach - 1));
 
       apply_overflow_at_weekday (self, left_attach);
     }
 
   update_overflow (self);
-
-  g_clear_pointer (&children, g_list_free);
 }
 
 static void
 header_expand (GcalWeekHeader *self)
 {
+  GtkLayoutManager *layout_manager;
   GtkWidget *week_view;
-  GList *children, *l;
+  GtkWidget *child;
 
-  children = gtk_container_get_children (GTK_CONTAINER (self->grid));
   week_view = gtk_widget_get_ancestor (GTK_WIDGET (self), GCAL_TYPE_WEEK_VIEW);
+  layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid));
 
   self->expanded = TRUE;
 
@@ -1069,33 +1103,35 @@ header_expand (GcalWeekHeader *self)
   gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (self->scrolledwindow),
                                               gtk_widget_get_allocated_height (week_view) / 2);
 
-  gtk_image_set_from_icon_name (GTK_IMAGE (self->expand_button_image), "go-up-symbolic", 4);
+  gtk_button_set_icon_name (self->expand_button, "go-up-symbolic");
 
-  for (l = children; l != NULL; l = l->next)
+  child = gtk_widget_get_first_child (GTK_WIDGET (self->grid));
+  while (child)
     {
+      GtkWidget *next = gtk_widget_get_next_sibling (child);
+
       /* Remove any remaining labels */
-      if (GTK_IS_LABEL (l->data))
+      if (GTK_IS_LABEL (child))
         {
-          gint left_attach;
+          GtkLayoutChild *layout_child;
+          gint column;
 
-          gtk_container_child_get (GTK_CONTAINER (self->grid),
-                                   l->data,
-                                   "left_attach", &left_attach,
-                                   NULL);
+          layout_child = gtk_layout_manager_get_layout_child (layout_manager, child);
+          column = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child));
 
-          self->overflow_label[left_attach] = NULL;
-          gtk_widget_destroy (l->data);
+          self->overflow_label[column] = NULL;
+          gtk_grid_remove (self->grid, child);
         }
       else
         {
-          gtk_widget_show (l->data);
+          gtk_widget_show (child);
         }
+
+      child = next;
     }
 
   /* Merge events that were broken because of the overflow label */
   check_mergeable_events (self);
-
-  g_clear_pointer (&children, g_list_free);
 }
 
 static void
@@ -1109,105 +1145,26 @@ on_expand_action_activated (GcalWeekHeader *self,
 }
 
 /* Drawing area content and size */
-static gdouble
-get_weekday_names_height (GtkWidget *widget)
-{
-  PangoFontDescription *font_desc;
-  GtkStyleContext* context;
-  GtkStateFlags state_flags;
-  PangoLayout *layout;
-  GtkBorder padding;
-  gint final_height;
-  gint font_height;
-
-  context = gtk_widget_get_style_context (widget);
-  state_flags = gtk_style_context_get_state (context);
-
-  layout = gtk_widget_create_pango_layout (widget, "A");
-
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, "week-dates");
-  gtk_style_context_get (context, state_flags, "font", &font_desc, NULL);
-  gtk_style_context_get_padding (context, state_flags, &padding);
-
-  pango_layout_set_font_description (layout, font_desc);
-  pango_layout_get_pixel_size (layout, NULL, &font_height);
-
-  pango_font_description_free (font_desc);
-  gtk_style_context_restore (context);
-
-  final_height = padding.top + font_height + padding.bottom;
-
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, "week-names");
-  gtk_style_context_get (context, state_flags, "font", &font_desc, NULL);
-  gtk_style_context_get_padding (context, state_flags, &padding);
-
-  pango_layout_set_font_description (layout, font_desc);
-  pango_layout_get_pixel_size (layout, NULL, &font_height);
-
-  pango_font_description_free (font_desc);
-  gtk_style_context_restore (context);
-
-  final_height += padding.top + font_height + padding.bottom;
-
-  return final_height;
-}
 
 static void
-gcal_week_header_finalize (GObject *object)
-{
-  GcalWeekHeader *self = GCAL_WEEK_HEADER (object);
-  gint i;
-
-  gcal_clear_date_time (&self->active_date);
-
-  for (i = 0; i < 7; i++)
-    g_list_free (self->events[i]);
-
-  for (i = 0; i < G_N_ELEMENTS (self->weather_infos); i++)
-    wid_clear (&self->weather_infos[i]);
-
-  G_OBJECT_CLASS (gcal_week_header_parent_class)->finalize (object);
-}
-
-static void
-gcal_week_header_size_allocate (GtkWidget     *widget,
-                                GtkAllocation *alloc)
-{
-  GcalWeekHeader *self = GCAL_WEEK_HEADER (widget);
-  gint min_header_height;
-
-  min_header_height = get_weekday_names_height (widget);
-
-  gtk_widget_set_margin_top (self->scrolledwindow, min_header_height);
-
-  GTK_WIDGET_CLASS (gcal_week_header_parent_class)->size_allocate (widget, alloc);
-}
-
-static gboolean
-gcal_week_header_draw (GtkWidget      *widget,
-                       cairo_t        *cr)
+gcal_week_header_snapshot (GtkWidget   *widget,
+                           GtkSnapshot *snapshot)
 {
   GtkStyleContext *context;
   GcalWeekHeader *self;
   GtkStateFlags state;
   GdkRGBA color;
   GtkAllocation alloc;
-  GtkBorder padding;
-
   GDateTime *week_start, *week_end;
-
-  PangoLayout *layout;
-  PangoFontDescription *bold_font;
-
-  gdouble cell_width;
-  gint i, day_abv_font_height, current_cell, today_column;
-  gint start_x, start_y;
-
   gboolean ltr;
-
-  cairo_save (cr);
+  gdouble cell_width;
+  gint current_cell;
+  gint start_x;
+  gint start_y;
+  gint height;
+  gint width;
+  gint x;
+  gint y;
 
   /* Fonts and colour selection */
   self = GCAL_WEEK_HEADER (widget);
@@ -1218,29 +1175,20 @@ gcal_week_header_draw (GtkWidget      *widget,
   start_x = ltr ? gtk_widget_get_allocated_width (self->expand_button_box) : 0;
   start_y = gtk_widget_get_allocated_height (self->header_labels_box);
 
-  gtk_style_context_get_padding (context, state, &padding);
   gtk_widget_get_allocation (widget, &alloc);
 
   if (!ltr)
     alloc.width -= gtk_widget_get_allocated_width (self->expand_button_box);
 
-  gtk_style_context_get_color (context, state, &color);
-  gdk_cairo_set_source_rgba (cr, &color);
-
-  layout = pango_cairo_create_layout (cr);
-  gtk_style_context_get (context, state, "font", &bold_font, NULL);
-  pango_font_description_set_weight (bold_font, PANGO_WEIGHT_MEDIUM);
-  pango_layout_set_font_description (layout, bold_font);
-
   week_start = gcal_date_time_get_start_of_week (self->active_date);
   week_end = g_date_time_add_days (week_start, 6);
   current_cell = g_date_time_get_day_of_week (self->active_date) - 1;
   current_cell = (7 + current_cell - self->first_weekday) % 7;
-  today_column = get_today_column (self);
 
   cell_width = (alloc.width - start_x) / 7.0;
 
   /* Drag and Drop highlight */
+#if 0 // TODO: DND
   if (self->dnd_cell != -1)
     {
       gtk_drag_highlight (widget);
@@ -1253,6 +1201,7 @@ gcal_week_header_draw (GtkWidget      *widget,
 
       gtk_drag_unhighlight (widget);
     }
+#endif
 
   /* Draw the selection background */
   if (self->selection_start != -1 && self->selection_end != -1)
@@ -1277,216 +1226,45 @@ gcal_week_header_draw (GtkWidget      *widget,
       selection_width = (end - start + 1) * cell_width;
       selection_x = ltr ? (start * cell_width) : (alloc.width - (start * cell_width + selection_width));
 
-      gtk_render_background (context,
-                             cr,
-                             ALIGNED (start_x + selection_x) + 0.33,
-                             start_y - 6,
-                             ALIGNED (selection_width + 1),
-                             alloc.height - start_y + 6);
+      gtk_snapshot_render_background (snapshot,
+                                      context,
+                                      ALIGNED (start_x + selection_x) + 0.33,
+                                      start_y - 6,
+                                      ALIGNED (selection_width + 1),
+                                      alloc.height - start_y + 6);
 
-      gtk_render_frame (context,
-                        cr,
-                        ALIGNED (start_x + selection_x) + 0.33,
-                        start_y - 6,
-                        ALIGNED (selection_width + 1),
-                        alloc.height - start_y + 6);
+      gtk_snapshot_render_frame (snapshot,
+                                 context,
+                                 ALIGNED (start_x + selection_x) + 0.33,
+                                 start_y - 6,
+                                 ALIGNED (selection_width + 1),
+                                 alloc.height - start_y + 6);
 
       gtk_style_context_restore (context);
     }
 
-  pango_layout_get_pixel_size (layout, NULL, &day_abv_font_height);
-
-  for (i = 0; i < 7; i++)
-    {
-      g_autoptr (GDateTime) day = NULL;
-      WeatherInfoDay *wdinfo; /* unowned */
-      gchar *weekday_date, *weekday_abv, *weekday;
-      gdouble x;
-      gint day_num_font_height, day_num_font_baseline;
-      gint font_width;
-      gint n_day;
-
-      day = g_date_time_add_days (week_start, i);
-      n_day = g_date_time_get_day_of_month (day);
-
-      if (n_day > g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year 
(week_start)))
-        n_day = n_day - g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year 
(week_start));
-
-      /* Draws the date of days in the week */
-      weekday_date = g_strdup_printf ("%d", n_day);
-
-      gtk_style_context_save (context);
-      gtk_style_context_add_class (context, "week-dates");
-      gtk_style_context_get (context, state, "font", &bold_font, NULL);
-
-      if (i == today_column)
-        gtk_style_context_add_class (context, "today");
-
-      pango_layout_set_font_description (layout, bold_font);
-      pango_layout_set_text (layout, weekday_date, -1);
-
-      pango_layout_get_pixel_size (layout, &font_width, &day_num_font_height);
-      day_num_font_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
-
-      if (ltr)
-        x = padding.left + cell_width * i + COLUMN_PADDING + start_x;
-      else
-        x = alloc.width - (cell_width * i + font_width + COLUMN_PADDING + start_x);
-
-      gtk_render_layout (context,
-                         cr,
-                         x,
-                         day_abv_font_height + padding.bottom + start_y,
-                         layout);
-
-      gtk_style_context_restore (context);
-
-      /* Draws the days name */
-      weekday = g_date_time_format (day, "%a");
-      weekday_abv = g_utf8_strup (weekday, -1);
-      g_free (weekday);
-
-      gtk_style_context_save (context);
-      gtk_style_context_add_class (context, "week-names");
-      gtk_style_context_get (context, state, "font", &bold_font, NULL);
-
-      if (i == today_column)
-        gtk_style_context_add_class (context, "today");
-
-      pango_layout_set_font_description (layout, bold_font);
-      pango_layout_set_text (layout, weekday_abv, -1);
-
-      pango_layout_get_pixel_size (layout, &font_width, NULL);
-
-      if (ltr)
-        x = padding.left + cell_width * i + COLUMN_PADDING + start_x;
-      else
-        x = alloc.width - (cell_width * i + font_width + COLUMN_PADDING + start_x);
-
-      gtk_render_layout (context,
-                         cr,
-                         x,
-                         start_y,
-                         layout);
-
-      gtk_style_context_restore (context);
-
-      /* Draws weather icon if given */
-      wdinfo = &self->weather_infos[ltr? i : 6 - i];
-      if (wdinfo->winfo != NULL)
-        {
-          const gchar *weather_icon_name = gcal_weather_info_get_icon_name (wdinfo->winfo);
-          const gchar *weather_temp = gcal_weather_info_get_temperature (wdinfo->winfo);
-
-          /* Imagine a box around weather indicators with length MAX(imgW,tempW)
-           * We compute its width and position in this section.
-           * Its height is derived from day name and numbers. The icon sticks on
-           * the very top and the temperature on its base.
-           */
-          gint icon_size = day_num_font_height - 3; /* available space - space between temperature and icon 
*/
-          gint temp_width = 0;
-          gint temp_height = 0;
-          gint temp_baseline = 0;
-
-          gdouble wibox_width;
-          gdouble wibox_x;
-
-          if (weather_icon_name != NULL && wdinfo->icon_buf == NULL)
-            {
-              GtkIconTheme *theme; /* unowned */
-              g_autoptr (GError) err = NULL;
-              gint icon_flags;
-
-              theme = gtk_icon_theme_get_default ();
-              /* TODO: catch icon theme changes */
-
-              icon_flags = ltr? GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_DIR_LTR
-                              : GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_DIR_RTL;
-              wdinfo->icon_buf = gtk_icon_theme_load_icon (theme, weather_icon_name, icon_size, icon_flags, 
&err);
-              if (err != NULL)
-                {
-                  g_assert (wdinfo->icon_buf == NULL);
-                  g_warning ("Could not load icon %s: %s", weather_icon_name, err->message);
-                }
-            }
-
-          if (G_LIKELY (weather_temp != NULL))
-            {
-              gtk_style_context_save (context);
-              gtk_style_context_add_class (context, "week-temperature");
-              gtk_style_context_get (context, state, "font", &bold_font, NULL);
-
-              pango_layout_set_font_description (layout, bold_font);
-              pango_layout_set_text (layout, weather_temp, -1);
-
-              pango_layout_get_pixel_size (layout, &temp_width, &temp_height);
-              temp_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
-            }
-
-          wibox_width = MAX(icon_size, temp_width);
-          wibox_x = ltr ? alloc.width - (cell_width * (6 - i) + wibox_width + COLUMN_PADDING)
-                        : padding.left + cell_width * i + COLUMN_PADDING + start_x;
-
-          /* Actually draw weather indicator: */
-          if (G_LIKELY (weather_temp != NULL))
-            {
-              gtk_render_layout (context,
-                                 cr,
-                                 wibox_x + (wibox_width - temp_width) / 2,
-                                 day_abv_font_height + padding.bottom + start_y + day_num_font_baseline - 
temp_baseline,
-                                 layout);
-              gtk_style_context_restore (context);
-            }
-
-          if (G_LIKELY (wdinfo->icon_buf != NULL))
-            {
-              gdk_cairo_set_source_pixbuf (cr,
-                                           wdinfo->icon_buf,
-                                           wibox_x + (wibox_width - icon_size) / 2,
-                                           start_y);
-              cairo_paint (cr);
-            }
-        }
-
-
-      /* Draws the lines after each day of the week */
-      gtk_style_context_save (context);
-      gtk_style_context_add_class (context, "lines");
-
-      gtk_style_context_get_color (context, state, &color);
-      gdk_cairo_set_source_rgba (cr, &color);
-
-      cairo_set_line_width (cr, 0.25);
-
-      cairo_move_to (cr,
-                     ALIGNED (ltr ? (cell_width * i + start_x) : (alloc.width - (cell_width * i + start_x))),
-                     day_abv_font_height + padding.bottom + start_y);
-
-      cairo_rel_line_to (cr,
-                         0.0,
-                         gtk_widget_get_allocated_height (widget) - day_abv_font_height - start_y + 
padding.bottom);
-      cairo_stroke (cr);
+  x = ALIGNED (ltr ? start_x : alloc.width - start_x);
+  y = start_y;
+  width = gtk_widget_get_width (widget) - start_x;
+  height = gtk_widget_get_height (widget) - start_y;
 
-      gtk_style_context_restore (context);
-
-      g_free (weekday_date);
-      g_free (weekday_abv);
-    }
-
-  cairo_restore (cr);
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, "lines");
+  gtk_style_context_get_color (context, &color);
+  gtk_style_context_restore (context);
 
-  pango_font_description_free (bold_font);
-  g_object_unref (layout);
+  gtk_snapshot_save (snapshot);
+  gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y));
+  gcal_week_view_common_snapshot_hour_lines (widget, snapshot, GTK_ORIENTATION_HORIZONTAL, &color, width, 
height);
+  gtk_snapshot_restore (snapshot);
 
-  GTK_WIDGET_CLASS (gcal_week_header_parent_class)->draw (widget, cr);
+  GTK_WIDGET_CLASS (gcal_week_header_parent_class)->snapshot (widget, snapshot);
 
   g_clear_pointer (&week_start, g_date_time_unref);
   g_clear_pointer (&week_end, g_date_time_unref);
-
-  return FALSE;
 }
 
-
+#if 0 // TODO: DND
 static gint
 get_dnd_cell (GtkWidget *widget,
               gint       x,
@@ -1633,6 +1411,28 @@ gcal_week_header_drag_leave (GtkWidget      *widget,
 
   gtk_widget_queue_draw (widget);
 }
+#endif
+
+/*
+ * GObject overrides
+ */
+
+static void
+gcal_week_header_finalize (GObject *object)
+{
+  GcalWeekHeader *self = GCAL_WEEK_HEADER (object);
+  gint i;
+
+  gcal_clear_date_time (&self->active_date);
+
+  for (i = 0; i < 7; i++)
+    g_list_free (self->events[i]);
+
+  for (i = 0; i < G_N_ELEMENTS (self->weather_infos); i++)
+    wid_clear (&self->weather_infos[i]);
+
+  G_OBJECT_CLASS (gcal_week_header_parent_class)->finalize (object);
+}
 
 static void
 gcal_week_header_class_init (GcalWeekHeaderClass *kclass)
@@ -1642,11 +1442,7 @@ gcal_week_header_class_init (GcalWeekHeaderClass *kclass)
 
   object_class->finalize = gcal_week_header_finalize;
 
-  widget_class->draw = gcal_week_header_draw;
-  widget_class->size_allocate = gcal_week_header_size_allocate;
-  widget_class->drag_motion = gcal_week_header_drag_motion;
-  widget_class->drag_leave = gcal_week_header_drag_leave;
-  widget_class->drag_drop = gcal_week_header_drag_drop;
+  widget_class->snapshot = gcal_week_header_snapshot;
 
   signals[EVENT_ACTIVATED] = g_signal_new ("event-activated",
                                            GCAL_TYPE_WEEK_HEADER,
@@ -1660,13 +1456,14 @@ gcal_week_header_class_init (GcalWeekHeaderClass *kclass)
 
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, expand_button);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, expand_button_box);
-  gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, expand_button_image);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, grid);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, header_labels_box);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, month_label);
+  gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, motion_controller);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, scrolledwindow);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, sizegroup);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, week_label);
+  gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, weekdays_box);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, year_label);
 
   gtk_widget_class_bind_template_callback (widget_class, on_button_pressed);
@@ -1680,6 +1477,8 @@ gcal_week_header_class_init (GcalWeekHeaderClass *kclass)
 static void
 gcal_week_header_init (GcalWeekHeader *self)
 {
+  gint i;
+
   self->expanded = FALSE;
   self->selection_start = -1;
   self->selection_end = -1;
@@ -1688,17 +1487,42 @@ gcal_week_header_init (GcalWeekHeader *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  /* This is to avoid stray lines when adding and removing events */
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-  gtk_container_set_reallocate_redraws (GTK_CONTAINER (self), TRUE);
-G_GNUC_END_IGNORE_DEPRECATIONS
+  for (i = 0; i < 7; i++)
+    {
+      WeekdayHeader *header = &self->weekday_header[i];
+      GtkWidget *box;
+
+      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+      gtk_widget_set_margin_start (box, 6);
+
+      header->weekday_name_label = gtk_label_new ("");
+      gtk_widget_set_hexpand (header->weekday_name_label, TRUE);
+      gtk_widget_add_css_class (header->weekday_name_label, "heading");
+      gtk_widget_add_css_class (header->weekday_name_label, "dim-label");
+      gtk_label_set_xalign (GTK_LABEL (header->weekday_name_label), 0.0);
+      gtk_box_append (GTK_BOX (box), header->weekday_name_label);
+
+      header->day_number_label = gtk_label_new ("");
+      gtk_widget_set_hexpand (header->day_number_label, TRUE);
+      gtk_widget_add_css_class (header->day_number_label, "title-2");
+      gtk_widget_add_css_class (header->day_number_label, "dim-label");
+      gtk_label_set_xalign (GTK_LABEL (header->day_number_label), 0.0);
+      gtk_box_append (GTK_BOX (box), header->day_number_label);
+
+      gtk_box_append (self->weekdays_box, box);
+
+      /* Add 7 empty widget to the grid to ensure proper spacing */
+      gtk_grid_attach (self->grid, gtk_box_new (GTK_ORIENTATION_VERTICAL, 0), i, 0, 1, 1);
+    }
 
+#if 0 // TODO: DND
   /* Setup the week header as a drag n' drop destination */
   gtk_drag_dest_set (GTK_WIDGET (self),
                      0,
                      NULL,
                      0,
                      GDK_ACTION_MOVE);
+#endif
 }
 
 void
@@ -1811,7 +1635,7 @@ gcal_week_header_remove_event (GcalWeekHeader *self,
                                const gchar    *uuid)
 {
   g_autoptr (GcalEvent) removed_event = NULL;
-  GList *children, *l;
+  GtkWidget *child;
   gint weekday;
 
   g_return_if_fail (GCAL_IS_WEEK_HEADER (self));
@@ -1821,26 +1645,30 @@ gcal_week_header_remove_event (GcalWeekHeader *self,
   if (!removed_event)
     return;
 
-  children = gtk_container_get_children (GTK_CONTAINER (self->grid));
-
-  for (l = children; l != NULL; l = l->next)
+  child = gtk_widget_get_first_child (GTK_WIDGET (self->grid));
+  while (child)
     {
-      GcalEventWidget *child_widget;
-      GcalEvent *event;
+      GtkWidget *next = gtk_widget_get_next_sibling (child);
 
-      if (!GCAL_IS_EVENT_WIDGET (l->data))
-        continue;
+      if (GCAL_IS_EVENT_WIDGET (child))
+        {
+          GcalEventWidget *child_widget;
+          GcalEvent *event;
+
+          child_widget = GCAL_EVENT_WIDGET (child);
+          event = gcal_event_widget_get_event (child_widget);
 
-      child_widget = GCAL_EVENT_WIDGET (l->data);
-      event = gcal_event_widget_get_event (child_widget);
+          if (g_strcmp0 (uuid, gcal_event_get_uid (event)) == 0)
+            destroy_event_widget (self, child);
+        }
 
-      if (g_strcmp0 (uuid, gcal_event_get_uid (event)) == 0)
-        destroy_event_widget (self, l->data);
+      child = next;
     }
 
   /* Remove from the weekday's GList */
   for (weekday = 0; weekday < 7; weekday++)
     {
+      GList *l;
       gint event_position;
 
       l = self->events[weekday];
@@ -1855,7 +1683,6 @@ gcal_week_header_remove_event (GcalWeekHeader *self,
 
       /* Move remaining events up */
       move_events_at_column (self, UP, weekday, event_position);
-
     }
 
   /* Check if we eventually can merge events */
@@ -1863,8 +1690,6 @@ gcal_week_header_remove_event (GcalWeekHeader *self,
 
   /* And also update the overflow labels */
   update_overflow (self);
-
-  g_clear_pointer (&children, g_list_free);
 }
 
 GList*
@@ -1872,16 +1697,13 @@ gcal_week_header_get_children_by_uuid (GcalWeekHeader        *self,
                                        GcalRecurrenceModType  mod,
                                        const gchar           *uuid)
 {
-  GList *children, *result;
+  g_autolist (GcalEventWidget) result = NULL;
 
   GCAL_ENTRY;
 
-  children = gtk_container_get_children (GTK_CONTAINER (self->grid));
-  result = filter_event_list_by_uid_and_modtype (children, mod, uuid);
-
-  g_list_free (children);
+  result = filter_children_by_uid_and_modtype (GTK_WIDGET (self->grid), mod, uuid);
 
-  GCAL_RETURN (result);
+  GCAL_RETURN (g_steal_pointer (&result));
 }
 
 GtkSizeGroup*
diff --git a/src/gui/views/gcal-week-header.h b/src/gui/views/gcal-week-header.h
index 5a37940f..5d45b976 100644
--- a/src/gui/views/gcal-week-header.h
+++ b/src/gui/views/gcal-week-header.h
@@ -30,7 +30,7 @@ G_BEGIN_DECLS
 
 #define GCAL_TYPE_WEEK_HEADER (gcal_week_header_get_type())
 
-G_DECLARE_FINAL_TYPE (GcalWeekHeader, gcal_week_header, GCAL, WEEK_HEADER, GtkGrid)
+G_DECLARE_FINAL_TYPE (GcalWeekHeader, gcal_week_header, GCAL, WEEK_HEADER, GtkBox)
 
 void                 gcal_week_header_set_context                (GcalWeekHeader     *self,
                                                                   GcalContext        *context);
diff --git a/src/gui/views/gcal-week-header.ui b/src/gui/views/gcal-week-header.ui
index fbf001f2..262b9619 100644
--- a/src/gui/views/gcal-week-header.ui
+++ b/src/gui/views/gcal-week-header.ui
@@ -1,19 +1,17 @@
 <?xml version="1.0"?>
 <interface>
-  <template class="GcalWeekHeader" parent="GtkGrid">
-    <property name="visible">True</property>
+  <template class="GcalWeekHeader" parent="GtkBox">
     <property name="hexpand">True</property>
     <property name="vexpand">False</property>
+    <property name="orientation">vertical</property>
     <style>
       <class name="week-header" />
     </style>
     <child>
       <object class="GtkBox" id="header_labels_box">
-        <property name="visible">True</property>
         <property name="orientation">horizontal</property>
         <child>
           <object class="GtkLabel" id="month_label">
-            <property name="visible">True</property>
             <property name="label">Month</property>
             <property name="yalign">0</property>
             <style>
@@ -23,7 +21,6 @@
         </child>
         <child>
           <object class="GtkLabel" id="week_label">
-            <property name="visible">True</property>
             <property name="hexpand">True</property>
             <property name="xalign">0</property>
             <property name="yalign">0</property>
@@ -35,7 +32,6 @@
         </child>
         <child>
           <object class="GtkLabel" id="year_label">
-            <property name="visible">True</property>
             <property name="label">Year</property>
             <property name="yalign">0</property>
             <style>
@@ -44,159 +40,82 @@
           </object>
         </child>
       </object>
-      <packing>
-        <property name="left_attach">0</property>
-        <property name="top_attach">0</property>
-        <property name="width">1</property>
-        <property name="height">1</property>
-      </packing>
     </child>
     <child>
       <object class="GtkBox">
-        <property name="visible">True</property>
         <property name="orientation">horizontal</property>
         <child>
           <object class="GtkBox" id="expand_button_box">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
             <property name="hexpand">False</property>
             <child>
               <object class="GtkButton" id="expand_button">
-                <property name="can_focus">True</property>
+                <property name="visible">False</property>
                 <property name="hexpand">True</property>
                 <property name="halign">center</property>
                 <property name="valign">end</property>
                 <property name="margin-bottom">6</property>
+                <property name="icon_name">go-down-symbolic</property>
                 <signal name="clicked" handler="on_expand_action_activated" object="GcalWeekHeader" 
swapped="yes"/>
                 <style>
                   <class name="flat" />
                   <class name="circular" />
                 </style>
-                <child>
-                  <object class="GtkImage" id="expand_button_image">
-                    <property name="visible">True</property>
-                    <property name="icon_name">go-down-symbolic</property>
-                  </object>
-                </child>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="GtkEventBox">
-            <property name="visible">True</property>
-            <signal name="button-press-event" handler="on_button_pressed" object="GcalWeekHeader" 
swapped="yes" />
-            <signal name="motion-notify-event" handler="on_motion_notify" object="GcalWeekHeader" 
swapped="yes" />
-            <signal name="button-release-event" handler="on_button_released" object="GcalWeekHeader" 
swapped="yes" />
+          <object class="GtkBox">
+            <property name="orientation">vertical</property>
+
+            <!-- Gestures -->
+            <child>
+              <object class="GtkGestureClick">
+                <property name="button">1</property>
+                <signal name="pressed" handler="on_button_pressed" object="GcalWeekHeader" swapped="no" />
+                <signal name="released" handler="on_button_released" object="GcalWeekHeader" swapped="no" />
+              </object>
+            </child>
+            <child>
+              <object class="GtkEventControllerMotion" id="motion_controller">
+                <property name="propagation-phase">none</property>
+                <signal name="motion" handler="on_motion_notify" object="GcalWeekHeader" swapped="no" />
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkBox" id="weekdays_box">
+                <property name="homogeneous">True</property>
+              </object>
+            </child>
+
             <child>
               <object class="GtkScrolledWindow" id="scrolledwindow">
-                <property name="visible">True</property>
                 <property name="hscrollbar-policy">never</property>
                 <property name="vscrollbar-policy">never</property>
                 <property name="propagate-natural-height">True</property>
                 <property name="margin-bottom">2</property>
+
                 <child>
                   <object class="GtkViewport">
-                    <property name="visible">True</property>
-                    <property name="shadow_type">none</property>
                     <child>
                       <object class="GtkGrid" id="grid">
-                        <property name="visible">True</property>
                         <property name="column-homogeneous">True</property>
                         <property name="hexpand">True</property>
                         <property name="column-spacing">6</property>
                         <property name="row-spacing">2</property>
                         <property name="margin-start">6</property>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">0</property>
-                            <property name="top_attach">0</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="top_attach">0</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">2</property>
-                            <property name="top_attach">0</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">3</property>
-                            <property name="top_attach">0</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">4</property>
-                            <property name="top_attach">0</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">5</property>
-                            <property name="top_attach">0</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">6</property>
-                            <property name="top_attach">0</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
-                        </child>
                       </object>
                     </child>
                   </object>
                 </child>
+
               </object>
             </child>
+
           </object>
         </child>
       </object>
-      <packing>
-        <property name="left_attach">0</property>
-        <property name="top_attach">1</property>
-      </packing>
     </child>
   </template>
   <object class="GtkSizeGroup" id="sizegroup">
diff --git a/src/gui/views/gcal-week-hour-bar.c b/src/gui/views/gcal-week-hour-bar.c
new file mode 100644
index 00000000..037c8ab8
--- /dev/null
+++ b/src/gui/views/gcal-week-hour-bar.c
@@ -0,0 +1,144 @@
+/* gcal-week-hour-bar.c
+ *
+ * Copyright 2022 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "gcal-week-hour-bar.h"
+#include "gcal-week-view-common.h"
+
+#include <glib/gi18n.h>
+
+struct _GcalWeekHourBar
+{
+  GtkBox              parent_instance;
+
+  GtkLabel           *labels[24];
+
+  GcalContext        *context;
+};
+
+G_DEFINE_FINAL_TYPE (GcalWeekHourBar, gcal_week_hour_bar, GTK_TYPE_BOX)
+
+static void
+update_labels (GcalWeekHourBar *self)
+{
+  GcalTimeFormat time_format;
+  gint i;
+
+  time_format = gcal_context_get_time_format (self->context);
+
+  for (i = 0; i < 24; i++)
+    {
+      g_autofree gchar *hours = NULL;
+
+      if (time_format == GCAL_TIME_FORMAT_24H)
+        {
+          hours = g_strdup_printf ("%02d:00", i);
+        }
+      else
+        {
+          hours = g_strdup_printf ("%d %s",
+                                   i % 12 == 0 ? 12 : i % 12,
+                                   i >= 12 ? _("PM") : _("AM"));
+        }
+
+      gtk_label_set_label (self->labels[i], hours);
+    }
+}
+
+static void
+gcal_week_hour_bar_snapshot (GtkWidget   *widget,
+                             GtkSnapshot *snapshot)
+{
+  GtkStyleContext *context;
+  GtkWidget *child;
+  GdkRGBA color;
+
+  context = gtk_widget_get_style_context (widget);
+
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, "lines");
+  gtk_style_context_get_color (context, &color);
+  gtk_style_context_restore (context);
+
+  gcal_week_view_common_snapshot_hour_lines (widget,
+                                             snapshot,
+                                             GTK_ORIENTATION_VERTICAL,
+                                             &color,
+                                             gtk_widget_get_width (widget),
+                                             gtk_widget_get_height (widget));
+
+  for (child = gtk_widget_get_first_child (widget);
+       child;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      gtk_widget_snapshot_child (widget, child, snapshot);
+    }
+}
+
+static void
+gcal_week_hour_bar_class_init (GcalWeekHourBarClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  widget_class->snapshot = gcal_week_hour_bar_snapshot;
+
+  gtk_widget_class_set_css_name (widget_class, "weekhourbar");
+}
+
+static void
+gcal_week_hour_bar_init (GcalWeekHourBar *self)
+{
+  gint i;
+
+  g_object_set (self,
+                "orientation", GTK_ORIENTATION_VERTICAL,
+                "homogeneous", TRUE,
+                "spacing", 1,
+                NULL);
+
+
+  for (i = 0; i < 24; i++)
+    {
+      GtkWidget *label = gtk_label_new ("");
+
+      gtk_widget_add_css_class (label, "line");
+      gtk_widget_add_css_class (label, "dim-label");
+      gtk_widget_set_vexpand (label, TRUE);
+      gtk_label_set_yalign (GTK_LABEL (label), 0.20);
+      gtk_box_append (GTK_BOX (self), label);
+
+      self->labels[i] = GTK_LABEL (label);
+    }
+}
+
+void
+gcal_week_hour_bar_set_context (GcalWeekHourBar *self,
+                                GcalContext     *context)
+{
+  g_return_if_fail (GCAL_IS_WEEK_HOUR_BAR (self));
+
+  self->context = context;
+
+  g_signal_connect_object (context,
+                           "notify::time-format",
+                           G_CALLBACK (update_labels),
+                           self,
+                           G_CONNECT_SWAPPED);
+  update_labels (self);
+}
diff --git a/src/gui/views/gcal-week-hour-bar.h b/src/gui/views/gcal-week-hour-bar.h
new file mode 100644
index 00000000..7261287d
--- /dev/null
+++ b/src/gui/views/gcal-week-hour-bar.h
@@ -0,0 +1,35 @@
+/* gcal-week-hour-bar.h
+ *
+ * Copyright 2022 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "gcal-context.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GCAL_TYPE_WEEK_HOUR_BAR (gcal_week_hour_bar_get_type())
+G_DECLARE_FINAL_TYPE (GcalWeekHourBar, gcal_week_hour_bar, GCAL, WEEK_HOUR_BAR, GtkBox)
+
+void                 gcal_week_hour_bar_set_context              (GcalWeekHourBar    *self,
+                                                                  GcalContext        *context);
+
+G_END_DECLS
diff --git a/src/gui/views/gcal-week-view-common.c b/src/gui/views/gcal-week-view-common.c
new file mode 100644
index 00000000..dc7aea94
--- /dev/null
+++ b/src/gui/views/gcal-week-view-common.c
@@ -0,0 +1,87 @@
+/* gcal-week-view-common.c
+ *
+ * Copyright 2022 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "gcal-week-view-common.h"
+#include "gcal-utils.h"
+
+void
+gcal_week_view_common_snapshot_hour_lines (GtkWidget      *widget,
+                                           GtkSnapshot    *snapshot,
+                                           GtkOrientation  orientation,
+                                           const GdkRGBA  *line_color,
+                                           gint            width,
+                                           gint            height)
+{
+  GdkRGBA color;
+  gboolean ltr;
+  gdouble column_width;
+  guint i;
+
+  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
+  color = *line_color;
+
+  column_width = width / 7.0;
+
+  switch (orientation)
+    {
+    case GTK_ORIENTATION_HORIZONTAL:
+      for (i = 0; i < 7; i++)
+        {
+          gdouble x;
+
+          if (ltr)
+            x = column_width * i;
+          else
+            x = width - column_width * i;
+
+          gtk_snapshot_append_color (snapshot,
+                                     &color,
+                                     &GRAPHENE_RECT_INIT (x, 0.f, 1.0, height));
+        }
+      break;
+
+
+    case GTK_ORIENTATION_VERTICAL:
+      /* Main lines */
+      for (i = 1; i < 24; i++)
+        {
+          gtk_snapshot_append_color (snapshot,
+                                     line_color,
+                                     &GRAPHENE_RECT_INIT (0.f,
+                                                          ALIGNED ((height / 24.0) * i),
+                                                          width,
+                                                          1.0));
+        }
+
+      /* In-between lines */
+      color.alpha /= 2.0;
+      for (i = 0; i < 24; i++)
+        {
+          gdouble half_cell_height = (height / 24.0) / 2.0;
+          gtk_snapshot_append_color (snapshot,
+                                     &color,
+                                     &GRAPHENE_RECT_INIT (0.f,
+                                                          ALIGNED ((height / 24.0) * i + half_cell_height),
+                                                          width,
+                                                          1.0));
+        }
+      break;
+    }
+}
diff --git a/src/gui/views/gcal-week-view-common.h b/src/gui/views/gcal-week-view-common.h
new file mode 100644
index 00000000..232e7b95
--- /dev/null
+++ b/src/gui/views/gcal-week-view-common.h
@@ -0,0 +1,34 @@
+/* gcal-week-view-common.h
+ *
+ * Copyright 2022 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void                 gcal_week_view_common_snapshot_hour_lines   (GtkWidget          *widget,
+                                                                  GtkSnapshot        *snapshot,
+                                                                  GtkOrientation      orientation,
+                                                                  const GdkRGBA      *line_color,
+                                                                  gint                width,
+                                                                  gint                height);
+
+G_END_DECLS
diff --git a/src/gui/views/gcal-week-view.c b/src/gui/views/gcal-week-view.c
index 5f8e5452..e68f37ac 100644
--- a/src/gui/views/gcal-week-view.c
+++ b/src/gui/views/gcal-week-view.c
@@ -25,27 +25,21 @@
 #include "gcal-utils.h"
 #include "gcal-view.h"
 #include "gcal-week-header.h"
+#include "gcal-week-hour-bar.h"
 #include "gcal-week-grid.h"
 #include "gcal-week-view.h"
 
+#include <adwaita.h>
 #include <glib/gi18n.h>
 
 #include <math.h>
 
-#define ALL_DAY_CELLS_HEIGHT 40
-
-static const double dashed [] =
-{
-  5.0,
-  6.0
-};
-
 struct _GcalWeekView
 {
   GtkBox              parent;
 
   GtkWidget          *header;
-  GtkWidget          *hours_bar;
+  GcalWeekHourBar    *hours_bar;
   GtkWidget          *scrolled_window;
   GtkWidget          *week_grid;
 
@@ -54,6 +48,7 @@ struct _GcalWeekView
   GcalContext        *context;
 
   guint               scroll_grid_timeout_id;
+  gulong              stack_page_changed_id;
 
   gint                clicked_cell;
 };
@@ -87,16 +82,16 @@ on_event_activated (GcalWeekView *self,
 }
 
 static void
-stack_visible_child_changed_cb (GtkStack     *stack,
+stack_visible_child_changed_cb (AdwViewStack *stack,
                                 GParamSpec   *pspec,
                                 GcalWeekView *self)
 {
-  if (gtk_stack_get_visible_child (stack) != (GtkWidget*) self)
+  if (adw_view_stack_get_visible_child (stack) != (GtkWidget*) self)
     return;
 
   schedule_position_scroll (self);
 
-  g_signal_handlers_disconnect_by_func (stack, stack_visible_child_changed_cb, self);
+  g_clear_signal_handler (&self->stack_page_changed_id, stack);
 }
 
 /* Auxiliary methods */
@@ -115,15 +110,16 @@ update_grid_scroll_position (GcalWeekView *self)
   if (!gtk_widget_get_realized (self->scrolled_window) ||
       !gtk_widget_get_mapped (self->scrolled_window))
     {
-      GtkWidget *stack;
-
-      stack = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_STACK);
+      if (self->stack_page_changed_id == 0)
+        {
+          GtkWidget *stack = gtk_widget_get_ancestor (GTK_WIDGET (self), ADW_TYPE_VIEW_STACK);
 
-      g_signal_connect_object (stack,
-                               "notify::visible-child",
-                               G_CALLBACK (stack_visible_child_changed_cb),
-                               self,
-                               0);
+          self->stack_page_changed_id = g_signal_connect_object (stack,
+                                                                 "notify::visible-child",
+                                                                 G_CALLBACK (stack_visible_child_changed_cb),
+                                                                 self,
+                                                                 0);
+        }
 
       self->scroll_grid_timeout_id = 0;
 
@@ -260,59 +256,6 @@ gcal_view_interface_init (GcalViewInterface *iface)
   iface->get_previous_date = gcal_week_view_get_previous_date;
 }
 
-static void
-update_hours_sidebar_size (GcalWeekView *self)
-{
-  GtkStyleContext *context;
-  GtkStateFlags state;
-  GtkSizeGroup *sidebar_sizegroup;
-  GtkWidget *widget;
-  GtkBorder padding;
-
-  PangoLayout *layout;
-  PangoFontDescription *font_desc;
-
-  gint hours_12_width, hours_24_width, sidebar_width;
-  gint hours_12_height, hours_24_height, cell_height;
-
-  widget = GTK_WIDGET (self);
-  context = gtk_widget_get_style_context (widget);
-  state = gtk_style_context_get_state (context);
-
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, "hours");
-
-  gtk_style_context_get (context, state,
-                         "font", &font_desc,
-                         NULL);
-  gtk_style_context_get_padding (context, state, &padding);
-
-  layout = pango_layout_new (gtk_widget_get_pango_context (widget));
-  pango_layout_set_font_description (layout, font_desc);
-
-  pango_layout_set_text (layout, _("00 AM"), -1);
-  pango_layout_get_pixel_size (layout, &hours_12_width, &hours_12_height);
-
-  pango_layout_set_text (layout, _("00:00"), -1);
-  pango_layout_get_pixel_size (layout, &hours_24_width, &hours_24_height);
-
-  sidebar_width = MAX (hours_12_width, hours_24_width) + padding.left + padding.right;
-  cell_height = MAX (hours_12_height, hours_24_height) + padding.top + padding.bottom + 1;
-
-  gtk_style_context_restore (context);
-
-  /* Update the size requests */
-  gtk_widget_set_size_request (self->hours_bar,
-                               sidebar_width,
-                               48 * cell_height);
-
-  /* Sync with the week header sidebar */
-  sidebar_sizegroup = gcal_week_header_get_sidebar_size_group (GCAL_WEEK_HEADER (self->header));
-  gtk_size_group_add_widget (sidebar_sizegroup, self->hours_bar);
-
-  pango_font_description_free (font_desc);
-  g_object_unref (layout);
-}
 
 /*
  * GcalTimelineSubscriber iface
@@ -386,114 +329,10 @@ gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface)
   iface->remove_event = gcal_week_view_remove_event;
 }
 
-static gboolean
-gcal_week_view_draw_hours (GcalWeekView *self,
-                           cairo_t      *cr,
-                           GtkWidget    *widget)
-{
-  GtkStyleContext *context;
-  GcalTimeFormat time_format;
-  GtkStateFlags state;
-  GtkBorder padding;
-  GdkRGBA color;
-
-  gboolean ltr;
-  gint i, width, height;
-  gint font_width;
-
-  PangoLayout *layout;
-  PangoFontDescription *font_desc;
-
-  time_format = gcal_context_get_time_format (self->context);
-  context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-
-  gtk_style_context_save (context);
 
-  gtk_style_context_add_class (context, "hours");
-  gtk_style_context_get_color (context, state, &color);
-  gtk_style_context_get_padding (context, state, &padding);
-  gtk_style_context_get (context, state, "font", &font_desc, NULL);
-
-  layout = pango_cairo_create_layout (cr);
-  pango_layout_set_font_description (layout, font_desc);
-  gdk_cairo_set_source_rgba (cr, &color);
-
-  /* Gets the size of the widget */
-  width = gtk_widget_get_allocated_width (widget);
-  height = gtk_widget_get_allocated_height (widget);
-
-  /* Draws the hours in the sidebar */
-  for (i = 0; i < 24; i++)
-    {
-      gchar *hours;
-
-      if (time_format == GCAL_TIME_FORMAT_24H)
-        {
-          hours = g_strdup_printf ("%02d:00", i);
-        }
-      else
-        {
-          hours = g_strdup_printf ("%d %s",
-                                   i % 12 == 0 ? 12 : i % 12,
-                                   i >= 12 ? _("PM") : _("AM"));
-        }
-
-      pango_layout_set_text (layout, hours, -1);
-      pango_layout_get_pixel_size (layout, &font_width, NULL);
-
-      gtk_render_layout (context,
-                         cr,
-                         ltr ? padding.left : width - font_width - padding.right,
-                         (height / 24) * i + padding.top,
-                         layout);
-
-      g_free (hours);
-    }
-
-  gtk_style_context_restore (context);
-
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, "lines");
-  gtk_style_context_get_color (context, state, &color);
-
-  gdk_cairo_set_source_rgba (cr, &color);
-  cairo_set_line_width (cr, 0.65);
-
-  if (!ltr)
-    {
-      cairo_move_to (cr, 0.5, 0);
-      cairo_rel_line_to (cr, 0, height);
-    }
-
-  /* Draws the horizontal complete lines */
-  for (i = 1; i < 24; i++)
-    {
-      cairo_move_to (cr, 0, (height / 24) * i + 0.4);
-      cairo_rel_line_to (cr, width, 0);
-    }
-
-  cairo_stroke (cr);
-
-  cairo_set_dash (cr, dashed, 2, 0);
-
-  /* Draws the horizontal dashed lines */
-  for (i = 0; i < 24; i++)
-    {
-      cairo_move_to (cr, 0, (height / 24) * i + (height / 48) + 0.4);
-      cairo_rel_line_to (cr, width, 0);
-    }
-
-  cairo_stroke (cr);
-
-  gtk_style_context_restore (context);
-
-  pango_font_description_free (font_desc);
-  g_object_unref (layout);
-
-  return FALSE;
-}
+/*
+ * GObject overrides
+ */
 
 static void
 gcal_week_view_finalize (GObject       *object)
@@ -530,12 +369,7 @@ gcal_week_view_set_property (GObject       *object,
 
       gcal_week_grid_set_context (GCAL_WEEK_GRID (self->week_grid), self->context);
       gcal_week_header_set_context (GCAL_WEEK_HEADER (self->header), self->context);
-
-      g_signal_connect_object (self->context,
-                               "notify::time-format",
-                               G_CALLBACK (gtk_widget_queue_draw),
-                               self->hours_bar,
-                               G_CONNECT_SWAPPED);
+      gcal_week_hour_bar_set_context (self->hours_bar, self->context);
       break;
 
     default:
@@ -579,6 +413,7 @@ gcal_week_view_class_init (GcalWeekViewClass *klass)
 
   g_type_ensure (GCAL_TYPE_WEEK_GRID);
   g_type_ensure (GCAL_TYPE_WEEK_HEADER);
+  g_type_ensure (GCAL_TYPE_WEEK_HOUR_BAR);
 
   object_class->finalize = gcal_week_view_finalize;
   object_class->set_property = gcal_week_view_set_property;
@@ -594,7 +429,6 @@ gcal_week_view_class_init (GcalWeekViewClass *klass)
   gtk_widget_class_bind_template_child (widget_class, GcalWeekView, scrolled_window);
   gtk_widget_class_bind_template_child (widget_class, GcalWeekView, week_grid);
 
-  gtk_widget_class_bind_template_callback (widget_class, gcal_week_view_draw_hours);
   gtk_widget_class_bind_template_callback (widget_class, on_event_activated);
 
   gtk_widget_class_set_css_name (widget_class, "calendar-view");
@@ -603,8 +437,11 @@ gcal_week_view_class_init (GcalWeekViewClass *klass)
 static void
 gcal_week_view_init (GcalWeekView *self)
 {
+  GtkSizeGroup *size_group;
+
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  update_hours_sidebar_size (self);
+  size_group = gcal_week_header_get_sidebar_size_group (GCAL_WEEK_HEADER (self->header));
+  gtk_size_group_add_widget (size_group, GTK_WIDGET (self->hours_bar));
 }
 
diff --git a/src/gui/views/gcal-week-view.ui b/src/gui/views/gcal-week-view.ui
index 7540bcb8..8ac99f73 100644
--- a/src/gui/views/gcal-week-view.ui
+++ b/src/gui/views/gcal-week-view.ui
@@ -1,39 +1,31 @@
 <?xml version="1.0"?>
 <interface>
   <template class="GcalWeekView" parent="GtkBox">
-    <property name="visible">True</property>
     <property name="orientation">vertical</property>
     <style>
       <class name="week-view" />
     </style>
     <child>
       <object class="GcalWeekHeader" id="header">
-        <property name="visible">True</property>
         <signal name="event-activated" handler="on_event_activated" object="GcalWeekView" swapped="yes" />
       </object>
     </child>
     <child>
       <object class="GtkScrolledWindow" id="scrolled_window">
-        <property name="visible">True</property>
         <property name="hexpand">True</property>
         <property name="vexpand">True</property>
         <child>
           <object class="GtkViewport">
-            <property name="visible">True</property>
             <child>
               <object class="GtkBox">
-                <property name="visible">True</property>
                 <property name="orientation">horizontal</property>
                 <child>
-                  <object class="GtkDrawingArea" id="hours_bar">
-                    <property name="visible">True</property>
+                  <object class="GcalWeekHourBar" id="hours_bar">
                     <property name="height-request">2568</property>
-                    <signal name="draw" handler="gcal_week_view_draw_hours" object="GcalWeekView" 
swapped="yes" />
                   </object>
                 </child>
                 <child>
                   <object class="GcalWeekGrid" id="week_grid">
-                    <property name="visible">True</property>
                     <property name="hexpand">True</property>
                     <property name="vexpand">True</property>
                     <signal name="event-activated" handler="on_event_activated" object="GcalWeekView" 
swapped="yes" />
diff --git a/src/gui/views/meson.build b/src/gui/views/meson.build
index 96c5a037..2306458b 100644
--- a/src/gui/views/meson.build
+++ b/src/gui/views/meson.build
@@ -13,6 +13,8 @@ sources += files(
   'gcal-view.c',
   'gcal-week-grid.c',
   'gcal-week-header.c',
+  'gcal-week-hour-bar.c',
   'gcal-week-view.c',
+  'gcal-week-view-common.c',
   'gcal-year-view.c',
 )
diff --git a/src/theme/Adwaita.css b/src/theme/Adwaita.css
index c757f458..fc1872af 100644
--- a/src/theme/Adwaita.css
+++ b/src/theme/Adwaita.css
@@ -386,18 +386,12 @@ label.month-name {
     color: alpha(@theme_fg_color, 0.25);
 }
 
-.week-header.week-names, .week-header.week-temperature {
+.week-header.week-temperature {
     font-size: 10pt;
     font-weight: bold;
     color: alpha(@theme_fg_color, 0.55);
 }
 
-.week-header.week-dates {
-    font-size: 16pt;
-    font-weight: bold;
-    color: alpha(@theme_fg_color, 0.70);
-}
-
 .week-header.lines {
     color: alpha(@theme_fg_color, 0.4);
 }
@@ -406,37 +400,24 @@ label.month-name {
     color: alpha(@theme_fg_color, 0.6);
 }
 
-.week-header .today {
-    color: @theme_selected_bg_color;
-}
-
-.week-view {
-    padding: 0px;
-}
-
-.week-view .hours {
+weekhourbar > label {
     font-size: 10pt;
-    color: alpha(@theme_fg_color, 0.8);
-    padding: 8px 12px;
+    padding: 4px 6px;
 }
 
 .week-view .lines {
-    color: alpha(@theme_fg_color, 0.30);
+    color: alpha(@window_fg_color, 0.30);
 }
 
-weekgrid.now-strip {
-    background-color: alpha(@theme_selected_bg_color, 0.8);
+weekgrid > widget.now-strip {
+    background-color: alpha(@accent_bg_color, 0.8);
     margin: 0 0 0 1px;
     min-height: 2px;
 }
 
 weekgrid:selected,
 .week-header:selected {
-    background-color: alpha(@theme_selected_bg_color, 0.25);
-}
-
-.week-header:selected {
-    border: solid 1px alpha(@theme_selected_bg_color, 0.8);
+    background-color: alpha(@accent_bg_color, 0.25);
 }
 
 
diff --git a/src/utils/gcal-utils.c b/src/utils/gcal-utils.c
index e3b0d6d1..c15da2e4 100644
--- a/src/utils/gcal-utils.c
+++ b/src/utils/gcal-utils.c
@@ -1112,29 +1112,30 @@ is_workday (guint day)
 }
 
 GList*
-filter_event_list_by_uid_and_modtype (GList                 *widgets,
-                                      GcalRecurrenceModType  mod,
-                                      const gchar           *uid)
+filter_children_by_uid_and_modtype (GtkWidget             *widget,
+                                    GcalRecurrenceModType  mod,
+                                    const gchar           *uid)
 {
+  GtkWidget *child;
   GcalEvent *event;
   GList *result;
-  GList *l;
 
   event = NULL;
   result = NULL;
 
   /* First pass: find the GcalEvent */
-  for (l = widgets; l != NULL; l = l->next)
+  for (child = gtk_widget_get_first_child (widget);
+       child;
+       child = gtk_widget_get_next_sibling (child))
     {
       GcalEventWidget *event_widget;
       GcalEvent *ev;
 
-      event_widget = l->data;
-
       /* Safeguard against stray widgets */
-      if (!GCAL_IS_EVENT_WIDGET (event_widget))
+      if (!GCAL_IS_EVENT_WIDGET (child))
         continue;
 
+      event_widget = GCAL_EVENT_WIDGET (child);
       ev = gcal_event_widget_get_event (event_widget);
 
       /*
@@ -1161,17 +1162,18 @@ filter_event_list_by_uid_and_modtype (GList                 *widgets,
       id = e_cal_component_get_id (component);
       id_prefix = g_strdup_printf ("%s:%s", gcal_calendar_get_id (calendar), e_cal_component_id_get_uid 
(id));
 
-      for (l = widgets; l != NULL; l = l->next)
+      for (child = gtk_widget_get_first_child (widget);
+           child;
+           child = gtk_widget_get_next_sibling (child))
         {
           GcalEventWidget *event_widget;
           GcalEvent *ev;
 
-          event_widget = l->data;
-
           /* Safeguard against stray widgets */
-          if (!GCAL_IS_EVENT_WIDGET (event_widget))
+          if (!GCAL_IS_EVENT_WIDGET (child))
             continue;
 
+          event_widget = GCAL_EVENT_WIDGET (child);
           ev = gcal_event_widget_get_event (event_widget);
 
           if (g_str_equal (gcal_event_get_uid (ev), uid))
diff --git a/src/utils/gcal-utils.h b/src/utils/gcal-utils.h
index d93040cf..bc0eb3b6 100644
--- a/src/utils/gcal-utils.h
+++ b/src/utils/gcal-utils.h
@@ -116,7 +116,7 @@ gboolean             ask_recurrence_modification_type            (GtkWidget
 
 gboolean             is_workday                                  (guint                      day);
 
-GList*               filter_event_list_by_uid_and_modtype        (GList                 *widgets,
+GList*               filter_children_by_uid_and_modtype          (GtkWidget             *widget,
                                                                   GcalRecurrenceModType  mod,
                                                                   const gchar           *uid);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]