diff --git a/src/gcal-enums.h b/src/gcal-enums.h
index df71a6e5..3955a7b0 100644
--- a/src/gcal-enums.h
+++ b/src/gcal-enums.h
@@ -42,6 +42,7 @@ typedef enum
{
GCAL_WINDOW_VIEW_WEEK,
GCAL_WINDOW_VIEW_MONTH,
+ GCAL_WINDOW_VIEW_DAZZLING_MONTH,
} GcalWindowView;
typedef enum
diff --git a/src/gui/gcal-window.c b/src/gui/gcal-window.c
index 126b8b4e..41faefbf 100644
--- a/src/gui/gcal-window.c
+++ b/src/gui/gcal-window.c
@@ -25,6 +25,7 @@
#include "gcal-calendar-button.h"
#include "config.h"
#include "gcal-date-chooser.h"
+#include "gcal-dazzling-month-view.h"
#include "gcal-debug.h"
#include "gcal-event-editor-dialog.h"
#include "gcal-event-widget.h"
@@ -104,6 +105,7 @@ struct _GcalWindow
AdwViewStack *views_stack;
GtkWidget *week_view;
GtkWidget *month_view;
+ GtkWidget *dazzling_month_view;
GtkWidget *agenda_view;
GtkWidget *date_chooser;
GtkWidget *action_bar;
@@ -204,6 +206,7 @@ update_today_action_enabled (GcalWindow *window)
break;
case GCAL_WINDOW_VIEW_MONTH:
+ case GCAL_WINDOW_VIEW_DAZZLING_MONTH:
enabled = g_date_time_get_year (window->active_date) != g_date_time_get_year (now) ||
g_date_time_get_month (window->active_date) != g_date_time_get_month (now);
break;
@@ -877,6 +880,7 @@ gcal_window_constructed (GObject *object)
g_object_bind_property (self, "context", self->calendar_management_dialog, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_bind_property (self, "context", self->week_view, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_bind_property (self, "context", self->month_view, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+ g_object_bind_property (self, "context", self->dazzling_month_view, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_bind_property (self, "context", self->agenda_view, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_bind_property (self, "context", self->date_chooser, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
g_object_bind_property (self, "context", self->event_editor, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
@@ -1012,6 +1016,7 @@ gcal_window_class_init (GcalWindowClass *klass)
g_type_ensure (GCAL_TYPE_CALENDAR_MANAGEMENT_DIALOG);
g_type_ensure (GCAL_TYPE_CALENDAR_BUTTON);
g_type_ensure (GCAL_TYPE_DATE_CHOOSER);
+ g_type_ensure (GCAL_TYPE_DAZZLING_MONTH_VIEW);
g_type_ensure (GCAL_TYPE_EVENT_EDITOR_DIALOG);
g_type_ensure (GCAL_TYPE_MANAGER);
g_type_ensure (GCAL_TYPE_MONTH_VIEW);
@@ -1065,6 +1070,7 @@ gcal_window_class_init (GcalWindowClass *klass)
gtk_widget_class_bind_template_child (widget_class, GcalWindow, agenda_view);
gtk_widget_class_bind_template_child (widget_class, GcalWindow, calendars_button);
gtk_widget_class_bind_template_child (widget_class, GcalWindow, date_chooser);
+ gtk_widget_class_bind_template_child (widget_class, GcalWindow, dazzling_month_view);
gtk_widget_class_bind_template_child (widget_class, GcalWindow, event_editor);
gtk_widget_class_bind_template_child (widget_class, GcalWindow, header_bar);
gtk_widget_class_bind_template_child (widget_class, GcalWindow, main_box);
@@ -1123,6 +1129,7 @@ gcal_window_init (GcalWindow *self)
self->views[GCAL_WINDOW_VIEW_WEEK] = self->week_view;
self->views[GCAL_WINDOW_VIEW_MONTH] = self->month_view;
+ self->views[GCAL_WINDOW_VIEW_DAZZLING_MONTH] = self->dazzling_month_view;
self->active_date = g_date_time_new_from_unix_local (0);
self->rtl = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
diff --git a/src/gui/gcal-window.ui b/src/gui/gcal-window.ui
index 50a67aec..9fd9d867 100644
--- a/src/gui/gcal-window.ui
+++ b/src/gui/gcal-window.ui
@@ -280,6 +280,22 @@
+
+
+
diff --git a/src/gui/views/gcal-dazzling-month-view.c b/src/gui/views/gcal-dazzling-month-view.c
new file mode 100644
index 00000000..2b11efc1
--- /dev/null
+++ b/src/gui/views/gcal-dazzling-month-view.c
@@ -0,0 +1,885 @@
+/*
+ * gcal-dazzling-month-view.c
+ *
+ * Copyright 2023 Georges Basile Stavracas Neto
+ *
+ * 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 .
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "GcalDazzlingMonthView"
+
+#include "config.h"
+#include "gcal-context.h"
+#include "gcal-dazzling-month-view.h"
+#include "gcal-debug.h"
+#include "gcal-month-view-row.h"
+#include "gcal-utils.h"
+#include "gcal-view-private.h"
+
+#include
+#include
+
+#define N_ROWS_PER_PAGE 5
+#define N_PAGES 5
+#define N_TOTAL_ROWS (N_ROWS_PER_PAGE * N_PAGES)
+
+// N_PAGES must be an odd number
+G_STATIC_ASSERT (N_PAGES % 2 != 0);
+
+struct _GcalDazzlingMonthView
+{
+ GtkWidget parent;
+
+ GtkWidget *header;
+ GtkWidget *month_label;
+ GtkWidget *year_label;
+ GtkWidget *weekday_label[7];
+
+ GPtrArray *week_rows;
+
+ /* Ranges from [0.0, 1.0] */
+ gdouble row_offset;
+ AdwAnimation *row_offset_animation;
+
+ AdwAnimation *kinetic_scroll_animation;
+ gdouble last_velocity;
+
+ GDateTime *date;
+ GcalContext *context;
+};
+
+static void gcal_view_interface_init (GcalViewInterface *iface);
+
+static void gtk_buildable_interface_init (GtkBuildableIface *iface);
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GcalDazzlingMonthView, gcal_dazzling_month_view, GTK_TYPE_WIDGET,
+ G_IMPLEMENT_INTERFACE (GCAL_TYPE_VIEW, gcal_view_interface_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_buildable_interface_init))
+
+enum
+{
+ PROP_0,
+ PROP_CONTEXT,
+ PROP_DATE,
+ N_PROPS,
+};
+
+
+/*
+ * Auxiliary methods
+ */
+
+static void
+update_header_labels (GcalDazzlingMonthView *self)
+{
+ gchar year_str[10] = { 0, };
+
+ g_snprintf (year_str, 10, "%d", g_date_time_get_year (self->date));
+
+ gtk_label_set_label (GTK_LABEL (self->month_label), gcal_get_month_name (g_date_time_get_month (self->date) - 1));
+ gtk_label_set_label (GTK_LABEL (self->year_label), year_str);
+}
+
+static inline void
+update_weekday_labels (GcalDazzlingMonthView *self)
+{
+ const gint first_weekday = get_first_weekday ();
+
+ for (gint i = 0; i < 7; i++)
+ {
+ g_autofree gchar *weekday_name = NULL;
+
+ weekday_name = g_utf8_strup (gcal_get_weekday ((i + first_weekday) % 7), -1);
+
+ gtk_label_set_label (GTK_LABEL (self->weekday_label[i]), weekday_name);
+ }
+}
+
+static void
+update_week_ranges (GcalDazzlingMonthView *self)
+{
+ gint n_weeks_before;
+
+ /*
+ * The current date corresponds to the week of the first
+ * visible row.
+ */
+ n_weeks_before = N_ROWS_PER_PAGE * (N_PAGES - 1) / 2;
+
+ for (gint i = 0; i < self->week_rows->len; i++)
+ {
+ g_autoptr (GDateTime) week_start = NULL;
+ g_autoptr (GDateTime) week_end = NULL;
+ g_autoptr (GcalRange) range = NULL;
+ g_autoptr (GDateTime) date = NULL;
+ GcalMonthViewRow *row;
+
+ date = g_date_time_add_weeks (self->date, i - n_weeks_before);
+ week_start = gcal_date_time_get_start_of_week (date);
+ week_end = gcal_date_time_get_end_of_week (date);
+ range = gcal_range_new (week_start, week_end, GCAL_RANGE_DATE_ONLY);
+
+ row = g_ptr_array_index (self->week_rows, i);
+ gcal_month_view_row_set_range (row, range);
+ }
+}
+
+static void
+cancel_deceleration (GcalDazzlingMonthView *self)
+{
+ if (self->kinetic_scroll_animation == 0)
+ return;
+
+ adw_animation_pause (self->kinetic_scroll_animation);
+ g_clear_object (&self->kinetic_scroll_animation);
+}
+
+static void
+cancel_row_offset_animation (GcalDazzlingMonthView *self)
+{
+ if (!self->row_offset_animation)
+ return;
+
+ adw_animation_pause (self->row_offset_animation);
+ g_clear_object (&self->row_offset_animation);
+}
+
+static void
+update_active_date (GcalDazzlingMonthView *self)
+{
+ g_autoptr (GcalRange) top_row_range = NULL;
+ GtkWidget *top_row;
+
+ top_row = g_ptr_array_index (self->week_rows, N_ROWS_PER_PAGE * (N_PAGES - 1) / 2);
+ top_row_range = gcal_month_view_row_get_range (GCAL_MONTH_VIEW_ROW (top_row));
+ g_assert (top_row_range != NULL);
+
+ gcal_view_set_date (GCAL_VIEW (self), gcal_range_get_start (top_row_range));
+}
+
+static void
+move_bottom_row_to_top (GcalDazzlingMonthView *self)
+{
+ g_autoptr (GcalRange) first_row_range = NULL;
+ g_autoptr (GcalRange) new_range = NULL;
+ GDateTime *first_row_range_start;
+ GtkWidget *first_row;
+ GtkWidget *last_row;
+
+ first_row = g_ptr_array_index (self->week_rows, 0);
+ first_row_range = gcal_month_view_row_get_range (GCAL_MONTH_VIEW_ROW (first_row));
+ g_assert (first_row_range != NULL);
+ first_row_range_start = gcal_range_get_end (first_row_range);
+ g_assert (first_row_range_start != NULL);
+
+ new_range = gcal_range_new_take (g_date_time_add_weeks (first_row_range_start, -1),
+ g_date_time_ref (first_row_range_start),
+ GCAL_RANGE_DATE_ONLY);
+
+#ifdef GCAL_ENABLE_TRACE
+ {
+ g_autofree gchar *new_range_str = gcal_range_to_string (new_range);
+ GCAL_TRACE_MSG ("Moved bottom row to top, new range: %s", new_range_str);
+ }
+#endif
+
+ last_row = g_ptr_array_steal_index (self->week_rows, self->week_rows->len - 1);
+ gcal_month_view_row_set_range (GCAL_MONTH_VIEW_ROW (last_row), new_range);
+ g_ptr_array_insert (self->week_rows, 0, last_row);
+}
+
+static void
+move_top_row_to_bottom (GcalDazzlingMonthView *self)
+{
+ g_autoptr (GcalRange) last_row_range = NULL;
+ g_autoptr (GcalRange) new_range = NULL;
+ GDateTime *last_row_range_end;
+ GtkWidget *first_row;
+ GtkWidget *last_row;
+
+ last_row = g_ptr_array_index (self->week_rows, self->week_rows->len - 1);
+ last_row_range = gcal_month_view_row_get_range (GCAL_MONTH_VIEW_ROW (last_row));
+ g_assert (last_row_range != NULL);
+ last_row_range_end = gcal_range_get_end (last_row_range);
+ g_assert (last_row_range_end != NULL);
+
+ new_range = gcal_range_new_take (g_date_time_ref (last_row_range_end),
+ g_date_time_add_weeks (last_row_range_end, 1),
+ GCAL_RANGE_DATE_ONLY);
+
+#ifdef GCAL_ENABLE_TRACE
+ {
+ g_autofree gchar *new_range_str = gcal_range_to_string (new_range);
+ GCAL_TRACE_MSG ("Moved top row to bottom, new range: %s", new_range_str);
+ }
+#endif
+
+ first_row = g_ptr_array_steal_index (self->week_rows, 0);
+ gcal_month_view_row_set_range (GCAL_MONTH_VIEW_ROW (first_row), new_range);
+ g_ptr_array_insert (self->week_rows, -1, first_row);
+}
+
+static inline void
+dump_row_ranges (GcalDazzlingMonthView *self)
+{
+#if defined(GCAL_ENABLE_TRACE) && 0
+ for (guint i = 0; i < self->week_rows->len; i++)
+ {
+ g_autoptr (GcalRange) range = NULL;
+ g_autofree gchar *range_str = NULL;
+ GtkWidget *row;
+
+ row = g_ptr_array_index (self->week_rows, i);
+ range = gcal_month_view_row_get_range (GCAL_MONTH_VIEW_ROW (row));
+ range_str = gcal_range_to_string (range);
+
+ GCAL_TRACE_MSG (" Row %u: %s", i, range_str);
+ }
+#endif
+}
+
+static inline gint
+get_grid_height (GcalDazzlingMonthView *self)
+{
+ return gtk_widget_get_height (GTK_WIDGET (self)) - gtk_widget_get_height (self->header);
+}
+
+static void
+offset_and_shuffle_rows (GcalDazzlingMonthView *self,
+ gdouble dy)
+{
+ gdouble row_height;
+ gint grid_height;
+
+ GCAL_ENTRY;
+
+ grid_height = get_grid_height (self);
+ row_height = grid_height / (gdouble) N_ROWS_PER_PAGE;
+ self->row_offset += dy / row_height;
+
+ if (fabs (self->row_offset) > 0.5)
+ {
+ gint rows_to_shuffle = round (fabs (self->row_offset) + 0.5);
+
+ g_assert (rows_to_shuffle > 0);
+
+ if (self->row_offset >= 0.0)
+ {
+ /* Positive offset: move top rows to the bottom */
+ while (rows_to_shuffle--)
+ move_top_row_to_bottom (self);
+ self->row_offset = fmod (self->row_offset, 0.5) - 0.5;
+ }
+ else
+ {
+ /* Negative offset: move bottom rows to the top */
+ while (rows_to_shuffle--)
+ move_bottom_row_to_top (self);
+ self->row_offset = fmod (self->row_offset, 0.5) + 0.5;
+ }
+
+ update_active_date (self);
+ dump_row_ranges (self);
+ }
+
+ gtk_widget_queue_allocate (GTK_WIDGET (self));
+
+ GCAL_EXIT;
+}
+
+static void
+animate_row_offset_cb (gdouble value,
+ gpointer user_data)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (user_data);
+
+ self->row_offset = value;
+ gtk_widget_queue_allocate (GTK_WIDGET (self));
+}
+
+static void
+on_row_offset_animation_done (AdwAnimation *animation,
+ GcalDazzlingMonthView *self)
+{
+ gtk_widget_remove_css_class (GTK_WIDGET (self), "scrolling");
+ cancel_row_offset_animation (self);
+}
+
+static void
+snap_to_top_row (GcalDazzlingMonthView *self)
+{
+ g_autoptr (AdwAnimationTarget) animation_target = NULL;
+
+ GCAL_ENTRY;
+
+ update_active_date (self);
+ dump_row_ranges (self);
+
+ animation_target = adw_callback_animation_target_new (animate_row_offset_cb, self, NULL);
+
+ g_clear_object (&self->row_offset_animation);
+ self->row_offset_animation = adw_timed_animation_new (GTK_WIDGET (self),
+ self->row_offset,
+ 0.0,
+ 200, // ms
+ g_steal_pointer (&animation_target));
+ adw_timed_animation_set_easing (ADW_TIMED_ANIMATION (self->row_offset_animation),
+ ADW_EASE_OUT_QUAD);
+ g_signal_connect (self->row_offset_animation,
+ "done",
+ G_CALLBACK (on_row_offset_animation_done),
+ self);
+
+ adw_animation_play (self->row_offset_animation);
+
+ GCAL_EXIT;
+}
+
+
+/*
+ * Callbacks
+ */
+
+static void
+on_scroll_controller_scroll_begin_cb (GtkEventControllerScroll *scroll_controller,
+ GcalDazzlingMonthView *self)
+{
+ GdkEvent *event;
+
+ GCAL_ENTRY;
+
+ event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (scroll_controller));
+ if (gdk_event_get_event_type (event) != GDK_TOUCHPAD_HOLD ||
+ gdk_touchpad_event_get_n_fingers (event) > 1)
+ {
+ gtk_widget_add_css_class (GTK_WIDGET (self), "scrolling");
+ cancel_row_offset_animation (self);
+ cancel_deceleration (self);
+ }
+
+ GCAL_EXIT;
+}
+
+static gboolean
+on_scroll_controller_scroll_cb (GtkEventControllerScroll *scroll_controller,
+ gdouble dx,
+ gdouble dy,
+ GcalDazzlingMonthView *self)
+{
+ GCAL_ENTRY;
+
+ cancel_row_offset_animation (self);
+ cancel_deceleration (self);
+
+ offset_and_shuffle_rows (self, dy);
+
+ GCAL_RETURN (GDK_EVENT_STOP);
+}
+
+static void
+on_scroll_controller_scroll_end_cb (GtkEventControllerScroll *scroll_controller,
+ GcalDazzlingMonthView *self)
+{
+ GCAL_ENTRY;
+
+ snap_to_top_row (self);
+
+ GCAL_EXIT;
+}
+
+static void
+decelerate_scroll_cb (gdouble value,
+ gpointer user_data)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (user_data);
+ gdouble dy;
+
+ dy = self->last_velocity - value;
+ self->last_velocity = value;
+
+ if (fabs (value) > (get_grid_height (self) / (gdouble) N_ROWS_PER_PAGE / 5.0))
+ offset_and_shuffle_rows (self, dy);
+ else
+ adw_animation_skip (self->kinetic_scroll_animation);
+}
+
+static void
+on_kinetic_scroll_done_cb (AdwAnimation *animation,
+ GcalDazzlingMonthView *self)
+{
+ GCAL_ENTRY;
+
+ snap_to_top_row (self);
+
+ GCAL_EXIT;
+}
+
+static void
+on_scroll_controller_decelerate_cb (GtkEventControllerScroll *scroll_controller,
+ gdouble velocity_x,
+ gdouble velocity_y,
+ GcalDazzlingMonthView *self)
+{
+ g_autoptr (AdwAnimationTarget) animation_target = NULL;
+ gdouble row_height;
+ gdouble duration;
+ gint grid_height;
+
+ GCAL_ENTRY;
+
+ /* XXX: I don't understand, but this just feels better */
+ velocity_y /= 2.0;
+
+ cancel_row_offset_animation (self);
+ cancel_deceleration (self);
+
+ grid_height = get_grid_height (self);
+ if (fabs (velocity_y) < (grid_height / (gdouble) N_ROWS_PER_PAGE / 5.0))
+ {
+ snap_to_top_row (self);
+ GCAL_RETURN ();
+ }
+
+ duration = fabs (velocity_y) / (gdouble) gtk_widget_get_height (GTK_WIDGET (self));
+ animation_target = adw_callback_animation_target_new (decelerate_scroll_cb, self, NULL);
+
+ /* Hijack the velocity so that it always ends in an actual row */
+ row_height = grid_height / (gdouble) N_ROWS_PER_PAGE;
+ velocity_y -= fmod (velocity_y, row_height) + self->row_offset * row_height;
+
+ g_assert (self->kinetic_scroll_animation == NULL);
+ self->kinetic_scroll_animation = adw_timed_animation_new (GTK_WIDGET (self),
+ velocity_y,
+ 0.0,
+ duration * 1000, // ms
+ g_steal_pointer (&animation_target));
+ self->last_velocity = velocity_y;
+ adw_timed_animation_set_easing (ADW_TIMED_ANIMATION (self->kinetic_scroll_animation),
+ ADW_EASE_OUT_EXPO);
+
+ g_signal_connect (self->kinetic_scroll_animation,
+ "done",
+ G_CALLBACK (on_kinetic_scroll_done_cb),
+ self);
+
+ adw_animation_play (self->kinetic_scroll_animation);
+
+ GCAL_EXIT;
+}
+
+
+/*
+ * GcalView interface
+ */
+
+static GDateTime*
+gcal_dazzling_month_view_get_date (GcalView *view)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (view);
+
+ return self->date;
+}
+
+static void
+gcal_dazzling_month_view_set_date (GcalView *view,
+ GDateTime *date)
+{
+ GcalDazzlingMonthView *self;
+ gboolean month_changed;
+
+ GCAL_ENTRY;
+
+ self = GCAL_DAZZLING_MONTH_VIEW (view);
+
+ month_changed = !self->date ||
+ !date ||
+ g_date_time_get_month (self->date) != g_date_time_get_month (date) ||
+ g_date_time_get_year (self->date) != g_date_time_get_year (date);
+
+ gcal_set_date_time (&self->date, date);
+
+ if (!month_changed)
+ GCAL_RETURN ();
+
+#ifdef GCAL_ENABLE_TRACE
+ {
+ g_autofree gchar *new_date_string = g_date_time_format (date, "%x %X %z");
+ GCAL_TRACE_MSG ("New date: %s", new_date_string);
+ }
+#endif
+
+ update_header_labels (self);
+ update_week_ranges (self);
+
+ GCAL_EXIT;
+}
+
+static void
+gcal_dazzling_month_view_clear_marks (GcalView *view)
+{
+ GCAL_TODO ("gcal_dazzling_month_view_clear_marks");
+}
+
+static GList*
+gcal_dazzling_month_view_get_children_by_uuid (GcalView *view,
+ GcalRecurrenceModType mod,
+ const gchar *uuid)
+{
+ return filter_children_by_uid_and_modtype (GTK_WIDGET (view), mod, uuid);
+}
+
+static GDateTime*
+gcal_dazzling_month_view_get_next_date (GcalView *view)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (view);
+
+ g_assert (self->date != NULL);
+ return g_date_time_add_months (self->date, 1);
+}
+
+
+static GDateTime*
+gcal_dazzling_month_view_get_previous_date (GcalView *view)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (view);
+
+ g_assert (self->date != NULL);
+ return g_date_time_add_months (self->date, -1);
+}
+
+static void
+gcal_view_interface_init (GcalViewInterface *iface)
+{
+ iface->get_date = gcal_dazzling_month_view_get_date;
+ iface->set_date = gcal_dazzling_month_view_set_date;
+ iface->clear_marks = gcal_dazzling_month_view_clear_marks;
+ iface->get_children_by_uuid = gcal_dazzling_month_view_get_children_by_uuid;
+ iface->get_next_date = gcal_dazzling_month_view_get_next_date;
+ iface->get_previous_date = gcal_dazzling_month_view_get_previous_date;
+}
+
+
+/*
+ * GtkBuildable interface
+ */
+
+static GtkBuildableIface *parent_buildable_iface;
+
+static void
+gcal_dazzling_month_view_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *type)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (buildable);
+
+ if (g_strcmp0 (type, "header") == 0)
+ {
+ self->header = GTK_WIDGET (child);
+ gtk_widget_set_parent (self->header, GTK_WIDGET (self));
+ }
+ else
+ {
+ parent_buildable_iface->add_child (buildable, builder, child, type);
+ }
+}
+
+static void
+gtk_buildable_interface_init (GtkBuildableIface *iface)
+{
+ parent_buildable_iface = g_type_interface_peek_parent (iface);
+
+ iface->add_child = gcal_dazzling_month_view_add_child;
+}
+
+
+/*
+ * GtkWidget overrides
+ */
+
+static void
+gcal_dazzling_month_view_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ gint for_size,
+ gint *minimum,
+ gint *natural,
+ gint *minimum_baseline,
+ gint *natural_baseline)
+{
+ GcalDazzlingMonthView *self;
+ gint natural_header_size;
+ gint minimum_header_size;
+ gint minimum_row_size;
+ gint natural_row_size;
+
+ self = GCAL_DAZZLING_MONTH_VIEW (widget);
+
+ gtk_widget_measure (self->header,
+ orientation,
+ for_size,
+ &minimum_header_size,
+ &natural_header_size,
+ NULL,
+ NULL);
+
+ for (guint i = 0; i < self->week_rows->len; i++)
+ {
+ GtkWidget *row;
+ gint row_minimum;
+ gint row_natural;
+
+ row = g_ptr_array_index (self->week_rows, i);
+
+ gtk_widget_measure (row,
+ orientation,
+ for_size,
+ &row_minimum,
+ &row_natural,
+ NULL,
+ NULL);
+
+ if (i == 0)
+ {
+ minimum_row_size = row_minimum;
+ natural_row_size = row_natural;
+ }
+ else
+ {
+ minimum_row_size = MIN (minimum_row_size, row_minimum);
+ natural_row_size = MIN (natural_row_size, row_natural);
+ }
+ }
+
+ if (minimum)
+ *minimum = minimum_header_size + minimum_row_size;
+
+ if (natural)
+ *natural = natural_header_size + natural_row_size;
+
+ if (minimum_baseline)
+ *minimum_baseline = -1;
+
+ if (natural_baseline)
+ *natural_baseline = -1;
+}
+
+static void
+gcal_dazzling_month_view_size_allocate (GtkWidget *widget,
+ gint width,
+ gint height,
+ gint baseline)
+{
+ GcalDazzlingMonthView *self;
+ gdouble row_scroll_offset;
+ gdouble row_height;
+ gdouble y_offset;
+ gint header_height;
+ gint grid_height;
+
+ self = GCAL_DAZZLING_MONTH_VIEW (widget);
+
+ gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, width, &header_height, NULL, NULL, NULL);
+ gtk_widget_allocate (self->header, width, header_height, baseline, NULL);
+
+ grid_height = height - header_height;
+ row_height = grid_height / (gdouble) N_ROWS_PER_PAGE;
+ row_scroll_offset = self->row_offset * row_height;
+ y_offset = header_height - row_scroll_offset - (grid_height * ((N_PAGES - 1) / 2.0));
+
+ for (guint i = 0; i < self->week_rows->len; i++)
+ {
+ GtkAllocation row_allocation;
+ GtkWidget *row;
+
+ row = g_ptr_array_index (self->week_rows, i);
+
+#define ROW_Y(_i) (row_height * (_i) + y_offset)
+
+ row_allocation.x = 0;
+ row_allocation.y = round (ROW_Y (i));
+ row_allocation.width = width;
+ row_allocation.height = round (ROW_Y (i + 1)) - row_allocation.y;
+
+#undef ROW_Y
+
+ gtk_widget_size_allocate (row, &row_allocation, baseline);
+ }
+}
+
+static void
+gcal_dazzling_month_view_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ GcalDazzlingMonthView *self;
+ gint header_height;
+ gint height;
+ gint width;
+
+ self = GCAL_DAZZLING_MONTH_VIEW (widget);
+
+ /* Snapshot rows with a clip */
+ width = gtk_widget_get_width (widget);
+ height = gtk_widget_get_height (widget);
+ header_height = gtk_widget_get_height (self->header);
+
+ gtk_snapshot_push_clip (snapshot,
+ &GRAPHENE_RECT_INIT (0, header_height, width, height - header_height));
+ for (guint i = 0; i < self->week_rows->len; i++)
+ gtk_widget_snapshot_child (widget, g_ptr_array_index (self->week_rows, i), snapshot);
+
+ gtk_snapshot_pop (snapshot);
+
+ /* Snapshot header */
+ gtk_widget_snapshot_child (widget, self->header, snapshot);
+}
+
+
+/*
+ * GObject overrides
+ */
+
+static void
+gcal_dazzling_month_view_dispose (GObject *object)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (object);
+
+ GCAL_ENTRY;
+
+ g_clear_object (&self->kinetic_scroll_animation);
+ g_clear_object (&self->row_offset_animation);
+
+ g_clear_pointer (&self->header, gtk_widget_unparent);
+ g_clear_pointer (&self->week_rows, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (gcal_dazzling_month_view_parent_class)->dispose (object);
+
+ GCAL_EXIT;
+}
+
+static void
+gcal_dazzling_month_view_finalize (GObject *object)
+{
+ GcalDazzlingMonthView *self = (GcalDazzlingMonthView *)object;
+
+ gcal_clear_date_time (&self->date);
+ g_clear_object (&self->context);
+
+ G_OBJECT_CLASS (gcal_dazzling_month_view_parent_class)->finalize (object);
+}
+
+static void
+gcal_dazzling_month_view_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_DATE:
+ g_value_set_boxed (value, self->date);
+ break;
+
+ case PROP_CONTEXT:
+ g_value_set_object (value, self->context);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcal_dazzling_month_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcalDazzlingMonthView *self = GCAL_DAZZLING_MONTH_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_DATE:
+ gcal_view_set_date (GCAL_VIEW (self), g_value_get_boxed (value));
+ break;
+
+ case PROP_CONTEXT:
+ g_assert (self->context == NULL);
+ self->context = g_value_dup_object (value);
+ g_object_notify (object, "context");
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcal_dazzling_month_view_class_init (GcalDazzlingMonthViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gcal_dazzling_month_view_dispose;
+ object_class->finalize = gcal_dazzling_month_view_finalize;
+ object_class->get_property = gcal_dazzling_month_view_get_property;
+ object_class->set_property = gcal_dazzling_month_view_set_property;
+
+ widget_class->measure = gcal_dazzling_month_view_measure;
+ widget_class->size_allocate = gcal_dazzling_month_view_size_allocate;
+ widget_class->snapshot = gcal_dazzling_month_view_snapshot;
+
+ g_object_class_override_property (object_class, PROP_CONTEXT, "context");
+ g_object_class_override_property (object_class, PROP_DATE, "active-date");
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/views/gcal-dazzling-month-view.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GcalDazzlingMonthView, month_label);
+ gtk_widget_class_bind_template_child (widget_class, GcalDazzlingMonthView, year_label);
+
+ gtk_widget_class_bind_template_child_full (widget_class, "label_0", FALSE, G_STRUCT_OFFSET (GcalDazzlingMonthView, weekday_label[0]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_1", FALSE, G_STRUCT_OFFSET (GcalDazzlingMonthView, weekday_label[1]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_2", FALSE, G_STRUCT_OFFSET (GcalDazzlingMonthView, weekday_label[2]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_3", FALSE, G_STRUCT_OFFSET (GcalDazzlingMonthView, weekday_label[3]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_4", FALSE, G_STRUCT_OFFSET (GcalDazzlingMonthView, weekday_label[4]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_5", FALSE, G_STRUCT_OFFSET (GcalDazzlingMonthView, weekday_label[5]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_6", FALSE, G_STRUCT_OFFSET (GcalDazzlingMonthView, weekday_label[6]));
+
+ gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_scroll_begin_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_scroll_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_scroll_end_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_decelerate_cb);
+
+ gtk_widget_class_set_css_name (widget_class, "calendar-view");
+}
+
+static void
+gcal_dazzling_month_view_init (GcalDazzlingMonthView *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+ update_weekday_labels (self);
+
+ self->week_rows = g_ptr_array_new_full (N_TOTAL_ROWS, (GDestroyNotify) gtk_widget_unparent);
+ for (gint i = 0; i < N_TOTAL_ROWS; i++)
+ {
+ GtkWidget *row = gcal_month_view_row_new ();
+ gtk_widget_set_parent (row, GTK_WIDGET (self));
+ g_ptr_array_add (self->week_rows, row);
+ }
+
+ gtk_widget_insert_after (self->header, GTK_WIDGET (self), NULL);
+}
diff --git a/src/gui/views/gcal-dazzling-month-view.h b/src/gui/views/gcal-dazzling-month-view.h
new file mode 100644
index 00000000..45bef5e2
--- /dev/null
+++ b/src/gui/views/gcal-dazzling-month-view.h
@@ -0,0 +1,31 @@
+/*
+ * gcal-dazzling-month-view.h
+ *
+ * Copyright 2023 Georges Basile Stavracas Neto
+ *
+ * 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 .
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include
+
+G_BEGIN_DECLS
+
+#define GCAL_TYPE_DAZZLING_MONTH_VIEW (gcal_dazzling_month_view_get_type())
+G_DECLARE_FINAL_TYPE (GcalDazzlingMonthView, gcal_dazzling_month_view, GCAL, DAZZLING_MONTH_VIEW, GtkWidget)
+
+G_END_DECLS
diff --git a/src/gui/views/gcal-dazzling-month-view.ui b/src/gui/views/gcal-dazzling-month-view.ui
new file mode 100644
index 00000000..d6a88d74
--- /dev/null
+++ b/src/gui/views/gcal-dazzling-month-view.ui
@@ -0,0 +1,130 @@
+
+
+
+ hidden
+
+
+
+
+ vertical|kinetic
+
+
+
+
+
+
+
+
+
+
+ vertical
+
+
+
+
+ true
+ 0.0
+
+
+
+
+
+ true
+ 1.0
+
+
+
+
+
+
+
+
+ 6
+
+
+ 6
+ true
+ 0.0
+
+
+
+
+
+
+ 6
+ true
+ 0.0
+
+
+
+
+
+
+ 6
+ true
+ 0.0
+
+
+
+
+
+
+ 6
+ true
+ 0.0
+
+
+
+
+
+
+ 6
+ true
+ 0.0
+
+
+
+
+
+
+ 6
+ true
+ 0.0
+
+
+
+
+
+
+ 6
+ true
+ 0.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/gui/views/gcal-month-cell.c b/src/gui/views/gcal-month-cell.c
index c48ecd63..1e9e5005 100644
--- a/src/gui/views/gcal-month-cell.c
+++ b/src/gui/views/gcal-month-cell.c
@@ -78,6 +78,7 @@ static void
update_style_flags (GcalMonthCell *self)
{
g_autoptr (GDateTime) today = NULL;
+ gint weekday;
/* Today */
today = g_date_time_new_now_local ();
@@ -86,6 +87,12 @@ update_style_flags (GcalMonthCell *self)
gtk_widget_add_css_class (GTK_WIDGET (self), "today");
else
gtk_widget_remove_css_class (GTK_WIDGET (self), "today");
+
+ weekday = g_date_time_get_day_of_week (self->date);
+ if (is_workday (weekday))
+ gtk_widget_add_css_class (GTK_WIDGET (self), "workday");
+ else
+ gtk_widget_remove_css_class (GTK_WIDGET (self), "workday");
}
static void
diff --git a/src/gui/views/gcal-month-view-row.c b/src/gui/views/gcal-month-view-row.c
new file mode 100644
index 00000000..83360f81
--- /dev/null
+++ b/src/gui/views/gcal-month-view-row.c
@@ -0,0 +1,262 @@
+/*
+ * gcal-month-view-row.c
+ *
+ * Copyright 2023 Georges Basile Stavracas Neto
+ *
+ * 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 .
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "GcalMonthViewRow"
+
+#include "config.h"
+#include "gcal-date-time-utils.h"
+#include "gcal-debug.h"
+#include "gcal-month-cell.h"
+#include "gcal-month-view-row.h"
+
+struct _GcalMonthViewRow
+{
+ GtkWidget parent;
+
+ GtkWidget *day_cells[7];
+
+ GcalRange *range;
+};
+
+G_DEFINE_FINAL_TYPE (GcalMonthViewRow, gcal_month_view_row, GTK_TYPE_WIDGET)
+
+enum
+{
+ PROP_0,
+ N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+
+/*
+ * GtkWidget overrides
+ */
+
+static void
+gcal_month_view_row_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ gint for_size,
+ gint *out_minimum,
+ gint *out_natural,
+ gint *out_minimum_baseline,
+ gint *out_natural_baseline)
+{
+ GcalMonthViewRow *self = GCAL_MONTH_VIEW_ROW (widget);
+ gint minimum = 0;
+ gint natural = 0;
+
+ for (guint i = 0; i < 7; i++)
+ {
+ gint child_minimum_baseline;
+ gint child_natural_baseline;
+ gint child_minimum;
+ gint child_natural;
+
+ gtk_widget_measure (self->day_cells[i],
+ orientation,
+ for_size,
+ &child_minimum,
+ &child_natural,
+ NULL,
+ NULL);
+
+
+ minimum += child_minimum;
+ natural += child_natural;
+ }
+
+ if (out_minimum)
+ *out_minimum = minimum;
+
+ if (out_natural)
+ *out_natural = natural;
+
+ if (out_minimum_baseline)
+ *out_minimum_baseline = -1;
+
+ if (out_natural_baseline)
+ *out_natural_baseline = -1;
+}
+
+static void
+gcal_month_view_row_size_allocate (GtkWidget *widget,
+ gint width,
+ gint height,
+ gint baseline)
+{
+ GcalMonthViewRow *self = GCAL_MONTH_VIEW_ROW (widget);
+ gboolean is_ltr;
+ gdouble cell_width;
+
+ is_ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
+ cell_width = width / 7.0;
+
+ for (guint i = 0; i < 7; i++)
+ {
+ GtkAllocation allocation;
+ GtkWidget *cell;
+
+ allocation.x = round (cell_width * i);
+ allocation.y = 0;
+ allocation.width = round (cell_width * (i + 1)) - allocation.x;
+ allocation.height = height;
+
+ cell = is_ltr ? self->day_cells[i] : self->day_cells[7 - i + 1];
+ gtk_widget_size_allocate (cell, &allocation, baseline);
+ }
+}
+
+static void
+gcal_month_view_row_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ GcalMonthViewRow *self = GCAL_MONTH_VIEW_ROW (widget);
+
+ for (guint i = 0; i < 7; i++)
+ gtk_widget_snapshot_child (widget, self->day_cells[i], snapshot);
+}
+
+
+/*
+ * GObject overrides
+ */
+
+static void
+gcal_month_view_row_dispose (GObject *object)
+{
+ GcalMonthViewRow *self = (GcalMonthViewRow *)object;
+
+ for (guint i = 0; i < 7; i++)
+ g_clear_pointer (&self->day_cells[i], gtk_widget_unparent);
+
+ G_OBJECT_CLASS (gcal_month_view_row_parent_class)->dispose (object);
+}
+
+static void
+gcal_month_view_row_finalize (GObject *object)
+{
+ GcalMonthViewRow *self = (GcalMonthViewRow *)object;
+
+ g_clear_pointer (&self->range, gcal_range_unref);
+
+ G_OBJECT_CLASS (gcal_month_view_row_parent_class)->finalize (object);
+}
+
+static void
+gcal_month_view_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GcalMonthViewRow *self = GCAL_MONTH_VIEW_ROW (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcal_month_view_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcalMonthViewRow *self = GCAL_MONTH_VIEW_ROW (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcal_month_view_row_class_init (GcalMonthViewRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gcal_month_view_row_dispose;
+ object_class->finalize = gcal_month_view_row_finalize;
+ object_class->get_property = gcal_month_view_row_get_property;
+ object_class->set_property = gcal_month_view_row_set_property;
+
+ widget_class->measure = gcal_month_view_row_measure;
+ widget_class->size_allocate = gcal_month_view_row_size_allocate;
+ widget_class->snapshot = gcal_month_view_row_snapshot;
+
+ gtk_widget_class_set_css_name (widget_class, "monthviewrow");
+}
+
+static void
+gcal_month_view_row_init (GcalMonthViewRow *self)
+{
+ for (guint i = 0; i < 7; i++)
+ {
+ self->day_cells[i] = gcal_month_cell_new ();
+ gcal_month_cell_set_overflow (GCAL_MONTH_CELL (self->day_cells[i]), 0);
+ gtk_widget_set_parent (self->day_cells[i], GTK_WIDGET (self));
+ }
+}
+
+GtkWidget *
+gcal_month_view_row_new (void)
+{
+ return g_object_new (GCAL_TYPE_MONTH_VIEW_ROW, NULL);
+}
+
+GcalRange *
+gcal_month_view_row_get_range (GcalMonthViewRow *self)
+{
+ g_return_val_if_fail (GCAL_IS_MONTH_VIEW_ROW (self), NULL);
+
+ return self->range ? gcal_range_ref (self->range) : NULL;
+}
+
+void
+gcal_month_view_row_set_range (GcalMonthViewRow *self,
+ GcalRange *range)
+{
+ g_autoptr (GDateTime) start = NULL;
+
+ g_return_if_fail (GCAL_IS_MONTH_VIEW_ROW (self));
+ g_return_if_fail (range != NULL);
+
+ GCAL_ENTRY;
+
+ if (self->range && gcal_range_compare (self->range, range) == 0)
+ GCAL_RETURN ();
+
+ g_clear_pointer (&self->range, gcal_range_unref);
+ self->range = gcal_range_ref (range);
+
+ /* TODO: remove all widgets */
+
+ start = gcal_range_get_start (range);
+ for (guint i = 0; i < 7; i++)
+ {
+ g_autoptr (GDateTime) day = g_date_time_add_days (start, i);
+ gcal_month_cell_set_date (GCAL_MONTH_CELL (self->day_cells[i]), day);
+ }
+}
diff --git a/src/gui/views/gcal-month-view-row.h b/src/gui/views/gcal-month-view-row.h
new file mode 100644
index 00000000..8e7e3634
--- /dev/null
+++ b/src/gui/views/gcal-month-view-row.h
@@ -0,0 +1,40 @@
+/*
+ * gcal-month-view-row.h
+ *
+ * Copyright 2023 Georges Basile Stavracas Neto
+ *
+ * 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 .
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include
+
+#include "gcal-range.h"
+
+G_BEGIN_DECLS
+
+#define GCAL_TYPE_MONTH_VIEW_ROW (gcal_month_view_row_get_type())
+G_DECLARE_FINAL_TYPE (GcalMonthViewRow, gcal_month_view_row, GCAL, MONTH_VIEW_ROW, GtkWidget)
+
+GtkWidget * gcal_month_view_row_new (void);
+
+GcalRange * gcal_month_view_row_get_range (GcalMonthViewRow *self);
+
+void gcal_month_view_row_set_range (GcalMonthViewRow *self,
+ GcalRange *range);
+
+G_END_DECLS
diff --git a/src/gui/views/meson.build b/src/gui/views/meson.build
index f68b3dc9..1df8125a 100644
--- a/src/gui/views/meson.build
+++ b/src/gui/views/meson.build
@@ -8,9 +8,11 @@ built_sources += gnome.compile_resources(
sources += files(
'gcal-agenda-view.c',
+ 'gcal-dazzling-month-view.c',
'gcal-month-cell.c',
'gcal-month-popover.c',
'gcal-month-view.c',
+ 'gcal-month-view-row.c',
'gcal-view.c',
'gcal-week-grid.c',
'gcal-week-header.c',
diff --git a/src/gui/views/views.gresource.xml b/src/gui/views/views.gresource.xml
index e1c21c87..ee68d8d3 100644
--- a/src/gui/views/views.gresource.xml
+++ b/src/gui/views/views.gresource.xml
@@ -2,6 +2,7 @@
gcal-agenda-view.ui
+ gcal-dazzling-month-view.ui
gcal-month-cell.ui
gcal-month-popover.ui
gcal-month-view.ui
diff --git a/src/theme/style.css b/src/theme/style.css
index f1c4a63c..91846602 100644
--- a/src/theme/style.css
+++ b/src/theme/style.css
@@ -317,10 +317,15 @@ monthcell:nth-child(7n + 1) { border-left-width: 0; }
monthcell.today { background-color: alpha(@accent_bg_color, 0.25); }
monthcell:selected.today { background-color: alpha(@accent_bg_color, 0.33); }
-monthcell.out-of-month {
+monthcell.out-of-month,
+monthcell:not(.workday) {
background-color: alpha(@theme_bg_color, 0.4);
}
+monthcell.today:not(.workday) {
+ background-color: alpha(@accent_bg_color, 0.15);
+}
+
monthcell label {
color: @theme_fg_color;
font-size: 0.9rem;
@@ -346,6 +351,21 @@ monthcell button {
border-right: none;
}
+/*
+ * Month row
+ */
+
+calendar-view.scrolling > box {
+ background-color: transparent;
+ box-shadow: @headerbar_shade_color 0 2px 4px;
+ transition: box-shadow 200ms;
+}
+
+
+calendar-view:not(.scrolling) > box {
+ transition: box-shadow 200ms;
+}
+
/*
* Month popover
*/