From b592f15b268afe83064d8739e5a8eed208544a58 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Wed, 30 Apr 2025 00:04:46 -0400 Subject: [PATCH 01/18] views/month-view: Add and update accessible elements Part-of: --- src/gui/views/gcal-month-cell.blp | 7 +++++++ src/gui/views/gcal-month-cell.c | 3 +++ src/gui/views/gcal-month-view-row.c | 3 +++ src/gui/views/gcal-month-view.blp | 1 + src/gui/views/gcal-month-view.c | 5 +++++ 5 files changed, 19 insertions(+) diff --git a/src/gui/views/gcal-month-cell.blp b/src/gui/views/gcal-month-cell.blp index b3c8666d5..e78eadf98 100644 --- a/src/gui/views/gcal-month-cell.blp +++ b/src/gui/views/gcal-month-cell.blp @@ -13,6 +13,13 @@ template $GcalMonthCell: Adw.BreakpointBin { } } + accessibility { + labelled-by: day_label; + has-popup: true; + selected: false; + } + + accessible-role: grid_cell; width-request: 50; height-request: 50; hexpand: true; diff --git a/src/gui/views/gcal-month-cell.c b/src/gui/views/gcal-month-cell.c index 01b15a3ef..248341609 100644 --- a/src/gui/views/gcal-month-cell.c +++ b/src/gui/views/gcal-month-cell.c @@ -624,6 +624,9 @@ gcal_month_cell_set_selected (GcalMonthCell *self, return; self->selected = selected; + gtk_accessible_update_state (GTK_ACCESSIBLE (self), + GTK_ACCESSIBLE_STATE_SELECTED, selected, + -1); /* Update style context state */ flags = gtk_widget_get_state_flags (GTK_WIDGET (self)); diff --git a/src/gui/views/gcal-month-view-row.c b/src/gui/views/gcal-month-view-row.c index 8648ed410..884b5310b 100644 --- a/src/gui/views/gcal-month-view-row.c +++ b/src/gui/views/gcal-month-view-row.c @@ -1111,6 +1111,9 @@ gcal_month_view_row_init (GcalMonthViewRow *self) gcal_month_cell_set_overflow (GCAL_MONTH_CELL (self->day_cells[i]), 0); gtk_widget_set_parent (self->day_cells[i], GTK_WIDGET (self)); g_signal_connect (self->day_cells[i], "show-overflow", G_CALLBACK (on_month_cell_show_overflow_cb), self); + gtk_accessible_update_relation (GTK_ACCESSIBLE (self->day_cells[i]), + GTK_ACCESSIBLE_RELATION_COL_INDEX, i + 1, + -1); } } diff --git a/src/gui/views/gcal-month-view.blp b/src/gui/views/gcal-month-view.blp index b9eb2328e..2297b2709 100644 --- a/src/gui/views/gcal-month-view.blp +++ b/src/gui/views/gcal-month-view.blp @@ -2,6 +2,7 @@ using Gtk 4.0; template $GcalMonthView: Widget { overflow: hidden; + accessible-role: grid; styles [ "month-view", diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index 2c453c26d..27f1fcf8f 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -1694,6 +1694,11 @@ gcal_month_view_init (GcalMonthView *self) gtk_widget_init_template (GTK_WIDGET (self)); update_weekday_labels (self); + gtk_accessible_update_relation (GTK_ACCESSIBLE (self), + GTK_ACCESSIBLE_RELATION_COL_COUNT, 7, + GTK_ACCESSIBLE_RELATION_ROW_COUNT, N_ROWS_PER_PAGE, + -1); + self->week_rows = g_ptr_array_new_full (N_TOTAL_ROWS, (GDestroyNotify) gtk_widget_unparent); for (gint i = 0; i < N_TOTAL_ROWS; i++) { -- GitLab From 80adbd0d23d1c6efa383244ad2eff6fbb09131b8 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Sat, 7 Feb 2026 10:47:08 -0500 Subject: [PATCH 02/18] views/month-cell: Inherit GtkWidget AdwBreakpointBin, the parent, cannot set focus to itself. Instead of focusing GtkOverlay, a child widget, we should instead inherit GtkWidget to allow focusing that instead. Part-of: --- src/gui/views/gcal-month-cell.blp | 135 +++++++++++++++--------------- src/gui/views/gcal-month-cell.c | 18 ++-- src/gui/views/gcal-month-cell.h | 2 +- 3 files changed, 81 insertions(+), 74 deletions(-) diff --git a/src/gui/views/gcal-month-cell.blp b/src/gui/views/gcal-month-cell.blp index e78eadf98..0fbfffad1 100644 --- a/src/gui/views/gcal-month-cell.blp +++ b/src/gui/views/gcal-month-cell.blp @@ -1,18 +1,7 @@ using Gtk 4.0; using Adw 1; -template $GcalMonthCell: Adw.BreakpointBin { - Adw.Breakpoint breakpoint_narrow { - condition ("max-width: 120sp") - - setters { - weather_icon.halign: end; - weather_icon.hexpand: true; - weather_icon.margin-start: 6; - temp_label.visible: false; - } - } - +template $GcalMonthCell: Widget { accessibility { labelled-by: day_label; has-popup: true; @@ -20,74 +9,86 @@ template $GcalMonthCell: Adw.BreakpointBin { } accessible-role: grid_cell; - width-request: 50; - height-request: 50; - hexpand: true; - vexpand: true; focusable: true; - Overlay overlay { - [overlay] - Box header_box { - valign: start; - spacing: 6; - margin-top: 6; - margin-bottom: 6; - margin-start: 6; - margin-end: 6; + Adw.BreakpointBin breakpoint_bin { + width-request: 50; + height-request: 50; - Label day_label { - styles [ - "day-label", - "numeric", - ] + Adw.Breakpoint breakpoint_narrow { + condition ("max-width: 120sp") + + setters { + weather_icon.halign: end; + weather_icon.hexpand: true; + weather_icon.margin-start: 6; + temp_label.visible: false; } + } - Image weather_icon { - pixel-size: 16; - margin-start: 12; - accessible-role: presentation; + child: Overlay overlay { + [overlay] + Box header_box { + valign: start; + spacing: 6; + margin-top: 6; + margin-bottom: 6; + margin-start: 6; + margin-end: 6; - styles [ - "dimmed", - ] - } + Label day_label { + styles [ + "day-label", + "numeric", + ] + } - Label temp_label { - styles [ - "dimmed", - "temp-label", - ] - } - } + Image weather_icon { + pixel-size: 16; + margin-start: 12; + accessible-role: presentation; - [overlay] - Button overflow_button { - accessible-role: toggle_button; - valign: end; - sensitive: false; - clicked => $overflow_button_clicked_cb(template) not-swapped; - tooltip-text: _("All Events"); - margin-top: 1; - margin-start: 1; - margin-bottom: 2; - margin-end: 2; + styles [ + "dimmed", + ] + } - accessibility { - has-popup: true; - labelled-by: overflow_inscription; + Label temp_label { + styles [ + "dimmed", + "temp-label", + ] + } } - styles [ - "flat", - "overflow", - ] + [overlay] + Button overflow_button { + accessible-role: toggle_button; + valign: end; + sensitive: false; + clicked => $overflow_button_clicked_cb(template) not-swapped; + tooltip-text: _("All Events"); + margin-top: 1; + margin-start: 1; + margin-bottom: 2; + margin-end: 2; - Box { - Inscription overflow_inscription { - text-overflow: ellipsize_end; + accessibility { + has-popup: true; + labelled-by: overflow_inscription; + } + + styles [ + "flat", + "overflow", + ] + + Box { + Inscription overflow_inscription { + text-overflow: ellipsize_end; + } } } - } + }; } } diff --git a/src/gui/views/gcal-month-cell.c b/src/gui/views/gcal-month-cell.c index 248341609..8cec95399 100644 --- a/src/gui/views/gcal-month-cell.c +++ b/src/gui/views/gcal-month-cell.c @@ -30,7 +30,7 @@ struct _GcalMonthCell { - AdwBreakpointBin parent; + GtkWidget parent; GDateTime *date; guint n_overflow; @@ -41,6 +41,7 @@ struct _GcalMonthCell GtkWidget *header_box; GtkImage *weather_icon; GtkLabel *temp_label; + GtkWidget *breakpoint_bin; GtkWidget *overflow_button; GtkInscription *overflow_inscription; @@ -56,7 +57,7 @@ struct _GcalMonthCell GBinding *icon_tooltip_binding; }; -G_DEFINE_TYPE (GcalMonthCell, gcal_month_cell, ADW_TYPE_BREAKPOINT_BIN) +G_DEFINE_TYPE (GcalMonthCell, gcal_month_cell, GTK_TYPE_WIDGET) enum { @@ -324,10 +325,10 @@ on_weather_service_weather_changed_cb (GcalWeatherService *weather_service, } static void -on_breakpoint_changed_cb (GcalMonthCell *self, - GParamSpec *pspec) +on_breakpoint_changed_cb (GcalMonthCell *self) { - AdwBreakpoint *current_breakpoint = adw_breakpoint_bin_get_current_breakpoint (ADW_BREAKPOINT_BIN (self)); + AdwBreakpoint *current_breakpoint = + adw_breakpoint_bin_get_current_breakpoint (ADW_BREAKPOINT_BIN (self->breakpoint_bin)); if (current_breakpoint == self->breakpoint_narrow) { @@ -357,6 +358,7 @@ gcal_month_cell_dispose (GObject *object) gcal_clear_date_time (&self->date); g_clear_object (&self->context); + g_clear_pointer (&self->breakpoint_bin, gtk_widget_unparent); G_OBJECT_CLASS (gcal_month_cell_parent_class)->dispose (object); } @@ -430,6 +432,7 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/views/gcal-month-cell.ui"); + gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, breakpoint_bin); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, breakpoint_narrow); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, day_label); gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, header_box); @@ -442,6 +445,8 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass) gtk_widget_class_bind_template_callback (widget_class, overflow_button_clicked_cb); gtk_widget_class_set_css_name (widget_class, "monthcell"); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); } static void @@ -456,7 +461,8 @@ gcal_month_cell_init (GcalMonthCell *self) g_signal_connect (drop_target, "drop", G_CALLBACK (on_drop_target_drop_cb), self); gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drop_target)); - g_signal_connect(self, "notify::current-breakpoint", G_CALLBACK (on_breakpoint_changed_cb), 0); + g_signal_connect_swapped (self->breakpoint_bin, + "notify::current-breakpoint", G_CALLBACK (on_breakpoint_changed_cb), self); } GtkWidget* diff --git a/src/gui/views/gcal-month-cell.h b/src/gui/views/gcal-month-cell.h index 2526c3c3b..be610449c 100644 --- a/src/gui/views/gcal-month-cell.h +++ b/src/gui/views/gcal-month-cell.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS #define GCAL_TYPE_MONTH_CELL (gcal_month_cell_get_type()) -G_DECLARE_FINAL_TYPE (GcalMonthCell, gcal_month_cell, GCAL, MONTH_CELL, AdwBreakpointBin) +G_DECLARE_FINAL_TYPE (GcalMonthCell, gcal_month_cell, GCAL, MONTH_CELL, GtkWidget) GtkWidget* gcal_month_cell_new (void); -- GitLab From e88f77802b267266844a5661dbb4be4e8aa93600 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Mon, 23 Feb 2026 21:23:03 -0500 Subject: [PATCH 03/18] views/month-cell: Override `focus()` virtual function Without this patch, focus immediately goes to the "Previous Month" button at startup, for some unknown reason. So, as a workaround, skip focus if the root cannot retrieve the current focused widget. Part-of: --- src/gui/views/gcal-month-cell.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/gui/views/gcal-month-cell.c b/src/gui/views/gcal-month-cell.c index 8cec95399..60d4eec75 100644 --- a/src/gui/views/gcal-month-cell.c +++ b/src/gui/views/gcal-month-cell.c @@ -344,6 +344,23 @@ on_breakpoint_changed_cb (GcalMonthCell *self) } +/* + * GtkWidget overrides + */ + +static gboolean +gcal_month_cell_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + GtkRoot *root = gtk_widget_get_root (widget); + + if (gtk_root_get_focus (root) == NULL) + return FALSE; + + return GTK_WIDGET_CLASS (gcal_month_cell_parent_class)->focus (widget, direction); +} + + /* * GObject overrides */ @@ -414,6 +431,8 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass) object_class->set_property = gcal_month_cell_set_property; object_class->get_property = gcal_month_cell_get_property; + widget_class->focus = gcal_month_cell_focus; + signals[SHOW_OVERFLOW] = g_signal_new ("show-overflow", GCAL_TYPE_MONTH_CELL, G_SIGNAL_RUN_LAST, -- GitLab From d76d37dff27de69862e2c1a04b4d3c966bce27b0 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Sat, 26 Apr 2025 21:53:44 -0400 Subject: [PATCH 04/18] views/month-cell: Add focus styling Part-of: --- src/theme/style.css | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/theme/style.css b/src/theme/style.css index ab7d742ff..f38a264f4 100644 --- a/src/theme/style.css +++ b/src/theme/style.css @@ -271,9 +271,12 @@ monthcell { box-shadow: 0 -1px var(--border-color), var(--offset) 0 var(--border-color); - - transition: background-color 200ms; + transition: + background-color 200ms, + var(--focus-ring-transition); background-color: var(--bg-color); + outline: 0 solid transparent; + outline-offset: 4px; } monthcell .overflow { @@ -310,6 +313,12 @@ monthcell:hover { background-color: rgb(from var(--bg-color) r g b / calc(alpha + 3.75%)); } +monthcell:focus:focus-visible { + outline-color: var(--focus-ring-color); + outline-width: 2px; + outline-offset: -2px; +} + /* Selected */ monthcell:selected { background-color: -- GitLab From 405f826252aa8a9167f492949534b396c271dfca Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 09:23:38 -0500 Subject: [PATCH 05/18] views/month-view-row: Add method for focusing adjacent cells This method allows to focus the month cells behind the event widgets. Part-of: --- src/gui/views/gcal-month-view-row.c | 17 +++++++++++++++++ src/gui/views/gcal-month-view-row.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/src/gui/views/gcal-month-view-row.c b/src/gui/views/gcal-month-view-row.c index 884b5310b..0dbd74d94 100644 --- a/src/gui/views/gcal-month-view-row.c +++ b/src/gui/views/gcal-month-view-row.c @@ -1273,3 +1273,20 @@ gcal_month_view_row_update_selection (GcalMonthViewRow *self, gcal_month_cell_set_selected (month_cell, selected); } } + +gboolean +gcal_month_view_row_focus_adjacent_cell (GcalMonthViewRow *self, + GtkWidget *widget) +{ + GtkWidget *cell; + g_autoptr (FocusEventData) data = NULL; + + g_assert (GCAL_IS_MONTH_VIEW_ROW (self)); + g_assert (gtk_widget_is_ancestor (widget, GTK_WIDGET (self))); + + data = create_focus_event_data (self, widget); + + cell = gcal_month_view_row_get_cell_at_x (self, data->focal_x); + + return gtk_widget_grab_focus (cell); +} diff --git a/src/gui/views/gcal-month-view-row.h b/src/gui/views/gcal-month-view-row.h index 25ef503a3..44aa6dd5c 100644 --- a/src/gui/views/gcal-month-view-row.h +++ b/src/gui/views/gcal-month-view-row.h @@ -59,4 +59,7 @@ GtkWidget* gcal_month_view_row_get_cell_at_x (GcalMonthViewR void gcal_month_view_row_update_selection (GcalMonthViewRow *self, GcalRange *selection_range); + +gboolean gcal_month_view_row_focus_adjacent_cell (GcalMonthViewRow *self, + GtkWidget *widget); G_END_DECLS -- GitLab From 48e6607d43556a4d69a2a5e986e454d79c8e02fc Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 09:32:20 -0500 Subject: [PATCH 06/18] views/month-view: Focus adjacent cell when pressing Ctrl+Tab Part-of: --- src/gui/views/gcal-month-view.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index 27f1fcf8f..e2de0e823 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -1342,7 +1342,7 @@ gcal_month_view_focus (GtkWidget *widget, { GcalMonthView *self = GCAL_MONTH_VIEW (widget); GtkWidget *candidate = NULL; - GtkWidget *focused; + GtkWidget *focused, *row; GtkRoot *root; root = gtk_widget_get_root (widget); @@ -1357,7 +1357,13 @@ gcal_month_view_focus (GtkWidget *widget, g_assert_nonnull (focused); gcal_month_popover_popdown (GCAL_MONTH_POPOVER (self->overflow.popover)); - return FALSE; + + if (gtk_widget_is_ancestor (focused, self->overflow.popover)) + return gtk_widget_grab_focus (self->overflow.relative_to); + + row = gtk_widget_get_ancestor (focused, GCAL_TYPE_MONTH_VIEW_ROW); + + return gcal_month_view_row_focus_adjacent_cell (GCAL_MONTH_VIEW_ROW (row), focused); } return GTK_WIDGET_CLASS (gcal_month_view_parent_class)->focus (widget, direction); -- GitLab From e6e201d9aae7e568aa84fad5b4dba41790c71c94 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 09:21:24 -0500 Subject: [PATCH 07/18] views/month-view-row: Implement focusing cells vertically Part-of: --- src/gui/views/gcal-month-view-row.c | 41 +++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/gui/views/gcal-month-view-row.c b/src/gui/views/gcal-month-view-row.c index 0dbd74d94..4369da5ef 100644 --- a/src/gui/views/gcal-month-view-row.c +++ b/src/gui/views/gcal-month-view-row.c @@ -409,6 +409,30 @@ is_overflow_focused (GtkWidget *focused) return gtk_widget_is_focus (overflow); } +static inline gboolean +focus_vertical_cell (GcalMonthViewRow *self, + GtkWidget *focused_cell) +{ + graphene_point_t point; + GtkWidget *row, *cell; + gdouble cell_width; + + if (gtk_widget_is_ancestor (focused_cell, GTK_WIDGET (self))) + return FALSE; + + cell_width = (gdouble) gtk_widget_get_width (GTK_WIDGET (self)) / 7.0; + row = gtk_widget_get_ancestor (focused_cell, GCAL_TYPE_MONTH_VIEW_ROW); + + g_assert (GTK_WIDGET (self) != row); + + if (!gtk_widget_compute_point (focused_cell, row, &GRAPHENE_POINT_INIT (cell_width / 2, 0), &point)) + g_assert_not_reached (); + + cell = gcal_month_view_row_get_cell_at_x (self, point.x); + + return gtk_widget_grab_focus (cell); +} + static void layout_block_free (gpointer data) { @@ -789,7 +813,9 @@ gcal_month_view_row_focus (GtkWidget *widget, GtkDirectionType direction) { GcalMonthViewRow *self = GCAL_MONTH_VIEW_ROW (widget); - GtkWidget *focused, *new_focus; + g_autoptr (FocusEventData) data = NULL; + GtkWidget *focused, *new_focus, *cell; + gboolean overflow_focused; GtkRoot *root; root = gtk_widget_get_root (widget); @@ -819,7 +845,18 @@ gcal_month_view_row_focus (GtkWidget *widget, } } - if (GCAL_IS_EVENT_WIDGET (focused) || is_overflow_focused (focused)) + overflow_focused = is_overflow_focused (focused); + cell = gtk_widget_get_ancestor (focused, GCAL_TYPE_MONTH_CELL); + + if (cell && !overflow_focused) + { + if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) + return focus_vertical_cell (self, cell); + else + return FALSE; + } + + if (GCAL_IS_EVENT_WIDGET (focused) || overflow_focused) { g_autoptr (FocusEventData) focus_event_data = create_focus_event_data (self, focused); -- GitLab From 471f81eb3105a8c5df9bb3735309814befd40949 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 09:22:37 -0500 Subject: [PATCH 08/18] views/month-view-row: Implement focusing cells horizontally Part-of: --- src/gui/views/gcal-month-view-row.c | 33 +++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/gui/views/gcal-month-view-row.c b/src/gui/views/gcal-month-view-row.c index 4369da5ef..2c450d8d2 100644 --- a/src/gui/views/gcal-month-view-row.c +++ b/src/gui/views/gcal-month-view-row.c @@ -433,6 +433,35 @@ focus_vertical_cell (GcalMonthViewRow *self, return gtk_widget_grab_focus (cell); } +static gboolean +focus_horizontal_cell (GcalMonthViewRow *self, + GtkWidget *focused_cell, + GtkDirectionType direction) +{ + gint width; + GtkWidget *new_cell; + graphene_point_t point; + + width = gtk_widget_get_width (focused_cell); + + if (!gtk_widget_compute_point (focused_cell, GTK_WIDGET (self), + &GRAPHENE_POINT_INIT (width / 2, 0), + &point)) + g_assert_not_reached (); + + if (direction == GTK_DIR_LEFT) + point.x -= width; + else + point.x += width; + + new_cell = gcal_month_view_row_get_cell_at_x (self, point.x); + + if (new_cell) + return gtk_widget_grab_focus (new_cell); + else + return FALSE; +} + static void layout_block_free (gpointer data) { @@ -852,8 +881,8 @@ gcal_month_view_row_focus (GtkWidget *widget, { if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) return focus_vertical_cell (self, cell); - else - return FALSE; + else if (direction == GTK_DIR_LEFT || direction == GTK_DIR_RIGHT) + return focus_horizontal_cell (self, cell, direction); } if (GCAL_IS_EVENT_WIDGET (focused) || overflow_focused) -- GitLab From cd0138393f7fe4d2a7ea0e5cba043e49ba1ac389 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 09:11:09 -0500 Subject: [PATCH 09/18] views/month-cell: Add activate signal This will be needed for keyboard navigation. Part-of: --- src/gui/views/gcal-month-cell.blp | 1 + src/gui/views/gcal-month-cell.c | 35 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/gui/views/gcal-month-cell.blp b/src/gui/views/gcal-month-cell.blp index 0fbfffad1..5ccc4c94c 100644 --- a/src/gui/views/gcal-month-cell.blp +++ b/src/gui/views/gcal-month-cell.blp @@ -9,6 +9,7 @@ template $GcalMonthCell: Widget { } accessible-role: grid_cell; + receives-default: true; focusable: true; Adw.BreakpointBin breakpoint_bin { diff --git a/src/gui/views/gcal-month-cell.c b/src/gui/views/gcal-month-cell.c index 60d4eec75..a2c619191 100644 --- a/src/gui/views/gcal-month-cell.c +++ b/src/gui/views/gcal-month-cell.c @@ -62,6 +62,7 @@ G_DEFINE_TYPE (GcalMonthCell, gcal_month_cell, GTK_TYPE_WIDGET) enum { SHOW_OVERFLOW, + ACTIVATE, N_SIGNALS }; @@ -441,6 +442,40 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass) 1, GTK_TYPE_WIDGET); + signals[ACTIVATE] = g_signal_new ("activate", + GCAL_TYPE_MONTH_CELL, + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE]); + + { + g_autoptr (GtkShortcutAction) activate_action = NULL; + const guint activate_keyvals[] = { + GDK_KEY_space, + GDK_KEY_KP_Space, + GDK_KEY_Return, + GDK_KEY_ISO_Enter, + GDK_KEY_KP_Enter, + }; + + activate_action = gtk_signal_action_new ("activate"); + + for (size_t i = 0; i < G_N_ELEMENTS (activate_keyvals); i++) + { + g_autoptr (GtkShortcut) activate_shortcut = NULL; + + activate_shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (activate_keyvals[i], 0), + g_object_ref (activate_action)); + + gtk_widget_class_add_shortcut (widget_class, activate_shortcut); + } + } + properties[PROP_CONTEXT] = g_param_spec_object ("context", "Context", "The GcalContext of the application", -- GitLab From 3088b2260aa0593582cc1c44bcc111e0d89c3fde Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 09:12:22 -0500 Subject: [PATCH 10/18] views/month-view-row: Add "activate-cell" signal Part-of: --- src/gui/views/gcal-month-view-row.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/gui/views/gcal-month-view-row.c b/src/gui/views/gcal-month-view-row.c index 2c450d8d2..bcdaa3671 100644 --- a/src/gui/views/gcal-month-view-row.c +++ b/src/gui/views/gcal-month-view-row.c @@ -65,6 +65,9 @@ static gint compare_events_cb (gconstpointer static void on_event_widget_activated_cb (GcalEventWidget *widget, GcalMonthViewRow *self); +static void on_cell_activated_cb (GcalMonthCell *cell, + GcalMonthViewRow *self); + static gboolean widget_tick_cb (GtkWidget *widget, GdkFrameClock *frame_clock, gpointer user_data); @@ -81,6 +84,7 @@ enum enum { EVENT_ACTIVATED, + CELL_ACTIVATED, SHOW_OVERFLOW, N_SIGNALS, }; @@ -795,6 +799,13 @@ on_event_widget_activated_cb (GcalEventWidget *widget, g_signal_emit (self, signals[EVENT_ACTIVATED], 0, widget); } +static void +on_cell_activated_cb (GcalMonthCell *cell, + GcalMonthViewRow *self) +{ + g_signal_emit (self, signals[CELL_ACTIVATED], 0, cell); +} + static void on_month_cell_show_overflow_cb (GcalMonthCell *cell, GtkWidget *button, @@ -1153,6 +1164,14 @@ gcal_month_view_row_class_init (GcalMonthViewRowClass *klass) 1, GCAL_TYPE_EVENT_WIDGET); + signals[CELL_ACTIVATED] = g_signal_new ("cell-activated", + GCAL_TYPE_MONTH_VIEW_ROW, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 1, + GCAL_TYPE_MONTH_CELL); + signals[SHOW_OVERFLOW] = g_signal_new ("show-overflow", GCAL_TYPE_MONTH_VIEW_ROW, G_SIGNAL_RUN_FIRST, @@ -1177,6 +1196,7 @@ gcal_month_view_row_init (GcalMonthViewRow *self) gcal_month_cell_set_overflow (GCAL_MONTH_CELL (self->day_cells[i]), 0); gtk_widget_set_parent (self->day_cells[i], GTK_WIDGET (self)); g_signal_connect (self->day_cells[i], "show-overflow", G_CALLBACK (on_month_cell_show_overflow_cb), self); + g_signal_connect (self->day_cells[i], "activate", G_CALLBACK (on_cell_activated_cb), self); gtk_accessible_update_relation (GTK_ACCESSIBLE (self->day_cells[i]), GTK_ACCESSIBLE_RELATION_COL_INDEX, i + 1, -1); -- GitLab From d8e44108eaeee33a58dd10ccfecc1a69a656324a Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 09:17:33 -0500 Subject: [PATCH 11/18] views/month-view: Display quick add popover when activating cell Part-of: --- src/gui/views/gcal-month-view.c | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index e2de0e823..ca4f88503 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -41,6 +41,7 @@ #define N_TOTAL_ROWS (N_ROWS_PER_PAGE * N_PAGES) #define FIRST_VISIBLE_ROW_INDEX (N_ROWS_PER_PAGE * (N_PAGES - 1) / 2) #define LAST_VISIBLE_ROW_INDEX (FIRST_VISIBLE_ROW_INDEX + N_ROWS_PER_PAGE - 1) +#define CELL_ACTIVATED_OFFSET 18.0 // N_PAGES must be an odd number G_STATIC_ASSERT (N_PAGES % 2 != 0); @@ -794,6 +795,49 @@ on_event_widget_activated_cb (GcalMonthViewRow *row, gcal_view_event_activated (GCAL_VIEW (self), event_widget); } +static void +on_month_row_cell_activated_cb (GcalMonthViewRow *row, + GcalMonthCell *cell, + GcalMonthView *self) +{ + g_autoptr (GcalRange) selection_range = NULL; + g_autoptr (GDateTime) selection_start = NULL; + g_autoptr (GDateTime) selection_end = NULL; + gint cell_width, cell_height; + graphene_point_t point; + + GCAL_ENTRY; + + self->selection.end = g_date_time_ref (gcal_month_cell_get_date (cell)); + + selection_start = self->selection.start ?: self->selection.end; + selection_end = self->selection.end; + + /* Swap dates if end is before start */ + if (gcal_date_time_compare_date (selection_start, selection_end) > 0) + { + GDateTime *aux = selection_end; + selection_end = selection_start; + selection_start = aux; + } + + cell_width = gtk_widget_get_width (GTK_WIDGET (cell)); + cell_height = gtk_widget_get_height (GTK_WIDGET (cell)); + + selection_start = g_date_time_ref (selection_start); + selection_end = g_date_time_add_days (selection_end, 1); + + if (!gtk_widget_compute_point (GTK_WIDGET (cell), GTK_WIDGET (self), + &GRAPHENE_POINT_INIT (cell_width / 2, cell_height - CELL_ACTIVATED_OFFSET), + &point)) + g_assert_not_reached (); + + selection_range = gcal_range_new (selection_start, selection_end, GCAL_RANGE_DATE_ONLY); + gcal_view_create_event (GCAL_VIEW (self), selection_range, point.x, point.y); + + GCAL_EXIT; +} + static gboolean on_key_controller_key_pressed_cb (GtkEventControllerKey *event_controller, guint keyval, @@ -1710,6 +1754,7 @@ gcal_month_view_init (GcalMonthView *self) { GtkWidget *row = gcal_month_view_row_new (); g_signal_connect (row, "event-activated", G_CALLBACK (on_event_widget_activated_cb), self); + g_signal_connect (row, "cell-activated", G_CALLBACK (on_month_row_cell_activated_cb), self); g_signal_connect (row, "show-overflow", G_CALLBACK (on_month_row_show_overflow_cb), self); gtk_widget_set_parent (row, GTK_WIDGET (self)); g_ptr_array_add (self->week_rows, row); -- GitLab From c74fe74b977d47117099f66d1c83c82f4bff3826 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 10:41:43 -0500 Subject: [PATCH 12/18] views/month-view: Wrap cells Part-of: --- src/gui/views/gcal-month-view.c | 62 ++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index ca4f88503..33db6a989 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -670,6 +670,61 @@ update_selection_range (GcalMonthView *self) } } +static gboolean +focus_month_cell (GcalMonthView *self, + GtkWidget *cell, + GtkDirectionType direction) +{ + GtkWidget *row, *new_row, *new_cell; + GtkDirectionType lateral; + gboolean is_rtl; + gint n_rows, new_cell_x; + guint row_index, new_row_index; + + row = gtk_widget_get_ancestor (cell, GCAL_TYPE_MONTH_VIEW_ROW); + + if (gtk_widget_child_focus (row, direction)) + return TRUE; + + is_rtl = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL; + + switch (direction) + { + case GTK_DIR_LEFT: + lateral = is_rtl ? GTK_DIR_DOWN : GTK_DIR_UP; + break; + case GTK_DIR_RIGHT: + lateral = is_rtl ? GTK_DIR_UP : GTK_DIR_DOWN; + break; + default: + break; + } + + if (!g_ptr_array_find (self->week_rows, row, &row_index)) + g_assert_not_reached (); + + if (direction == GTK_DIR_LEFT) + new_cell_x = gtk_widget_get_width (row) - gtk_widget_get_width (cell) / 2; + else if (direction == GTK_DIR_RIGHT) + new_cell_x = gtk_widget_get_width (cell) / 2; + else + return TRUE; + + if (lateral == GTK_DIR_UP) + n_rows = -1; + else + n_rows = 1; + + new_row_index = row_index + n_rows; + new_row = g_ptr_array_index (self->week_rows, new_row_index); + new_cell = gcal_month_view_row_get_cell_at_x (GCAL_MONTH_VIEW_ROW (new_row), new_cell_x); + + if (row_index < FIRST_VISIBLE_ROW_INDEX || row_index > LAST_VISIBLE_ROW_INDEX) + return TRUE; + + return gtk_widget_grab_focus (new_cell); +} + /* @@ -1428,7 +1483,12 @@ gcal_month_view_focus (GtkWidget *widget, g_assert (candidate == NULL || GTK_IS_WIDGET (candidate)); if (candidate == NULL) - return gtk_widget_keynav_failed (widget, direction); + { + if (GCAL_IS_MONTH_CELL (focused) && !(self->state & GDK_CONTROL_MASK)) + return focus_month_cell (self, focused, direction); + else + return gtk_widget_keynav_failed (widget, direction); + } return TRUE; } -- GitLab From 6c6dd46a893ffde2a45495d690a526f321e506cc Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 10:46:51 -0500 Subject: [PATCH 13/18] views/month-view: Scroll view when out of bounds Part-of: --- src/gui/views/gcal-month-view.c | 45 +++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index 33db6a989..0847c327f 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -670,6 +670,36 @@ update_selection_range (GcalMonthView *self) } } +static void +trigger_scroll (GcalMonthView *self, + gint n_rows) +{ + gtk_widget_add_css_class (GTK_WIDGET (self), "scrolling"); + + maybe_popdown_overflow_popover (self); + cancel_row_offset_animation (self); + cancel_deceleration (self); + + animate_row_scroll (self, n_rows); +} + +static gint +compute_horizontal_axis_of_cell (GcalMonthView *self, + GtkWidget *cell) +{ + gint cell_center; + graphene_point_t point; + + cell_center = gtk_widget_get_width (cell) / 2; + + if (!gtk_widget_compute_point (cell, GTK_WIDGET (self), + &GRAPHENE_POINT_INIT (cell_center, 0), + &point)) + g_assert_not_reached (); + + return point.x; +} + static gboolean focus_month_cell (GcalMonthView *self, GtkWidget *cell, @@ -697,6 +727,8 @@ focus_month_cell (GcalMonthView *self, lateral = is_rtl ? GTK_DIR_UP : GTK_DIR_DOWN; break; default: + g_assert (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN); + lateral = direction; break; } @@ -708,7 +740,7 @@ focus_month_cell (GcalMonthView *self, else if (direction == GTK_DIR_RIGHT) new_cell_x = gtk_widget_get_width (cell) / 2; else - return TRUE; + new_cell_x = compute_horizontal_axis_of_cell (self, cell); if (lateral == GTK_DIR_UP) n_rows = -1; @@ -722,6 +754,9 @@ focus_month_cell (GcalMonthView *self, if (row_index < FIRST_VISIBLE_ROW_INDEX || row_index > LAST_VISIBLE_ROW_INDEX) return TRUE; + if (new_row_index < FIRST_VISIBLE_ROW_INDEX || new_row_index > LAST_VISIBLE_ROW_INDEX) + trigger_scroll (self, n_rows); + return gtk_widget_grab_focus (new_cell); } @@ -977,13 +1012,7 @@ on_discrete_scroll_controller_scroll_cb (GtkEventControllerScroll *scroll_contro GCAL_RETURN (GDK_EVENT_PROPAGATE); } - gtk_widget_add_css_class (GTK_WIDGET (self), "scrolling"); - - maybe_popdown_overflow_popover (self); - cancel_row_offset_animation (self); - cancel_deceleration (self); - - animate_row_scroll (self, n_rows); + trigger_scroll (self, n_rows); GCAL_RETURN (GDK_EVENT_STOP); } -- GitLab From 3102766c517760812052ddce4bab1e66a29d8e2d Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 6 Feb 2026 10:47:44 -0500 Subject: [PATCH 14/18] views/month-view: Allow selecting cells Part-of: --- src/gui/views/gcal-month-view.blp | 1 + src/gui/views/gcal-month-view.c | 99 +++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/gui/views/gcal-month-view.blp b/src/gui/views/gcal-month-view.blp index 2297b2709..9be70d653 100644 --- a/src/gui/views/gcal-month-view.blp +++ b/src/gui/views/gcal-month-view.blp @@ -10,6 +10,7 @@ template $GcalMonthView: Widget { EventControllerKey { key-pressed => $on_key_controller_key_pressed_cb(template) not-swapped; + key-released => $on_key_controller_key_released_cb(template) not-swapped; } EventControllerScroll { diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index 0847c327f..1d62d86d8 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -670,6 +670,33 @@ update_selection_range (GcalMonthView *self) } } +static gboolean +child_focus_from_keyval (GtkWidget *widget, + guint keyval) +{ + switch (keyval) + { + case GDK_KEY_Up: + case GDK_KEY_KP_Up: + return gtk_widget_child_focus (widget, GTK_DIR_UP); + + case GDK_KEY_Right: + case GDK_KEY_KP_Right: + return gtk_widget_child_focus (widget, GTK_DIR_RIGHT); + + case GDK_KEY_Down: + case GDK_KEY_KP_Down: + return gtk_widget_child_focus (widget, GTK_DIR_DOWN); + + case GDK_KEY_Left: + case GDK_KEY_KP_Left: + return gtk_widget_child_focus (widget, GTK_DIR_LEFT); + + default: + return FALSE; + } +} + static void trigger_scroll (GcalMonthView *self, gint n_rows) @@ -935,11 +962,82 @@ on_key_controller_key_pressed_cb (GtkEventControllerKey *event_controller, GdkModifierType state, GcalMonthView *self) { + GDateTime *date = NULL; + GtkWidget *focused_widget, *month_cell; + GtkRoot *root; + + root = gtk_widget_get_root (GTK_WIDGET (self)); + focused_widget = gtk_root_get_focus (root); + month_cell = gtk_widget_get_ancestor (focused_widget, GCAL_TYPE_MONTH_CELL); + self->state = state; + if (month_cell && (state & GDK_SHIFT_MASK)) + { + if (keyval == GDK_KEY_Escape) + { + gcal_view_clear_marks (GCAL_VIEW (self)); + return GDK_EVENT_STOP; + } + + if (!child_focus_from_keyval (GTK_WIDGET (self), keyval)) + return GDK_EVENT_PROPAGATE; + + if (!self->selection.start) + { + date = gcal_month_cell_get_date (GCAL_MONTH_CELL (month_cell)); + self->selection.start = g_date_time_ref (date); + + update_selection_range (self); + } + + focused_widget = gtk_root_get_focus (root); + month_cell = gtk_widget_get_ancestor (focused_widget, GCAL_TYPE_MONTH_CELL); + + g_assert (GCAL_IS_MONTH_CELL (month_cell)); + + gcal_clear_date_time (&self->selection.end); + date = gcal_month_cell_get_date (GCAL_MONTH_CELL (month_cell)); + self->selection.end = g_date_time_ref (date); + + update_selection_range (self); + } + return GDK_EVENT_PROPAGATE; } +static void +on_key_controller_key_released_cb (GtkEventControllerKey *event_controller, + guint keyval, + guint keycode, + GdkModifierType state, + GcalMonthView *self) +{ + GtkWidget *focused_widget, *month_cell; + GtkRoot *root; + + root = gtk_widget_get_root (GTK_WIDGET (self)); + focused_widget = gtk_root_get_focus (root); + month_cell = gtk_widget_get_ancestor (focused_widget, GCAL_TYPE_MONTH_CELL); + + if (month_cell) + { + switch (keyval) + { + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: + case GDK_KEY_Shift_Lock: + if (!self->selection.end) + break; + + gtk_widget_activate (month_cell); + break; + default: + break; + } + } +} + static void on_scroll_controller_scroll_begin_cb (GtkEventControllerScroll *scroll_controller, GcalMonthView *self) @@ -1819,6 +1917,7 @@ gcal_month_view_class_init (GcalMonthViewClass *klass) 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_bind_template_callback (widget_class, on_key_controller_key_pressed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_key_controller_key_released_cb); gtk_widget_class_set_css_name (widget_class, "calendar-view"); } -- GitLab From 4e54c18d06e6796eb8284d232a3e25c323b09369 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Mon, 23 Feb 2026 23:43:06 -0500 Subject: [PATCH 15/18] views/month-view: Set focus to cell upon click release Part-of: --- src/gui/views/gcal-month-view.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index 1d62d86d8..a1ce020e2 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -833,7 +833,8 @@ on_click_gesture_released_cb (GtkGestureClick *click_gesture, g_autoptr (GcalRange) selection_range = NULL; g_autoptr (GDateTime) selection_start = NULL; g_autoptr (GDateTime) selection_end = NULL; - GtkWidget *widget_at_position; + GtkWidget *widget_at_position, *cell, *row; + GtkRoot *root; GCAL_ENTRY; @@ -843,12 +844,16 @@ on_click_gesture_released_cb (GtkGestureClick *click_gesture, if (!self->selection.start || !widget_at_position || - !gtk_widget_get_ancestor (widget_at_position, GCAL_TYPE_MONTH_VIEW_ROW)) + !(row = gtk_widget_get_ancestor (widget_at_position, GCAL_TYPE_MONTH_VIEW_ROW))) { gcal_view_clear_marks (GCAL_VIEW (self)); GCAL_RETURN (); } + root = gtk_widget_get_root (GTK_WIDGET (self)); + cell = gcal_month_view_row_get_cell_at_x (GCAL_MONTH_VIEW_ROW (row), x); + gtk_root_set_focus (root, cell); + selection_start = self->selection.start; selection_end = self->selection.end ?: selection_start; -- GitLab From d6a450ff1ff1c07aabc0bfd6b43250e6914ed126 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Mon, 23 Feb 2026 23:44:12 -0500 Subject: [PATCH 16/18] gui/window: Focus last focused widget upon closing quick add popover This also introduces the `focus_last_focused_widget()` to make the code a little bit cleaner. Part-of: --- src/gui/gcal-window.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/gui/gcal-window.c b/src/gui/gcal-window.c index 016b28994..b3c23393c 100644 --- a/src/gui/gcal-window.c +++ b/src/gui/gcal-window.c @@ -220,6 +220,16 @@ update_today_action_enabled (GcalWindow *window) GCAL_EXIT; } +static void +focus_last_focused_widget (GcalWindow *self) +{ + if (self->last_focused_widget) + { + gtk_widget_grab_focus (self->last_focused_widget); + g_clear_weak_pointer (&self->last_focused_widget); + } +} + static gchar* get_previous_date_icon (GcalWindow *window, GcalWindowView *view) @@ -764,7 +774,10 @@ set_new_event_mode (GcalWindow *window, g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_NEW_EVENT_MODE]); if (!enabled && window->views[window->active_view]) - gcal_view_clear_marks (GCAL_VIEW (window->views[window->active_view])); + { + gcal_view_clear_marks (GCAL_VIEW (window->views[window->active_view])); + focus_last_focused_widget (window); + } /* XXX: here we could disable clicks from the views, yet */ /* for now we relaunch the new-event widget */ @@ -786,11 +799,15 @@ show_new_event_widget (GcalView *view, { graphene_point_t p; GdkRectangle rect; + GtkRoot *root; GCAL_ENTRY; g_assert (range != NULL); + root = gtk_widget_get_root (GTK_WIDGET (window)); + g_set_weak_pointer (&window->last_focused_widget, gtk_root_get_focus (root)); + /* 1st and 2nd steps */ set_new_event_mode (window, TRUE); @@ -891,11 +908,7 @@ event_preview_cb (GcalEventWidget *event_widget, case GCAL_EVENT_PREVIEW_ACTION_NONE: default: - if (self->last_focused_widget) - { - gtk_root_set_focus (GTK_ROOT (self), self->last_focused_widget); - g_clear_weak_pointer (&self->last_focused_widget); - } + focus_last_focused_widget (self); break; } } -- GitLab From e607a6bd2ed806b3fd1493746027fcab3f349be9 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Tue, 24 Feb 2026 00:02:21 -0500 Subject: [PATCH 17/18] views/month-view: Clear marks when tabbing from a cell Part-of: --- src/gui/views/gcal-month-view.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index a1ce020e2..d10913bb6 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -1596,6 +1596,11 @@ gcal_month_view_focus (GtkWidget *widget, return gcal_month_view_row_focus_adjacent_cell (GCAL_MONTH_VIEW_ROW (row), focused); } + else if (focused && GCAL_IS_MONTH_CELL (focused)) + { + gcal_view_clear_marks (GCAL_VIEW (self)); + return FALSE; + } return GTK_WIDGET_CLASS (gcal_month_view_parent_class)->focus (widget, direction); } -- GitLab From 2638766d10f824b57522e9b8d05955c7cd78df83 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Tue, 24 Feb 2026 00:02:50 -0500 Subject: [PATCH 18/18] views/month-view: Focus adjacent cell when tabbing forward from the last event widget Part-of: --- src/gui/views/gcal-month-view.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c index d10913bb6..5ca589ee6 100644 --- a/src/gui/views/gcal-month-view.c +++ b/src/gui/views/gcal-month-view.c @@ -1596,10 +1596,22 @@ gcal_month_view_focus (GtkWidget *widget, return gcal_month_view_row_focus_adjacent_cell (GCAL_MONTH_VIEW_ROW (row), focused); } - else if (focused && GCAL_IS_MONTH_CELL (focused)) + else if (focused) { - gcal_view_clear_marks (GCAL_VIEW (self)); - return FALSE; + if (GCAL_IS_MONTH_CELL (focused)) + { + gcal_view_clear_marks (GCAL_VIEW (self)); + return FALSE; + } + + if (direction == GTK_DIR_TAB_FORWARD && GCAL_IS_EVENT_WIDGET (focused)) + { + if (GTK_WIDGET_CLASS (gcal_month_view_parent_class)->focus (widget, direction)) + return TRUE; + + row = gtk_widget_get_ancestor (focused, GCAL_TYPE_MONTH_VIEW_ROW); + return gcal_month_view_row_focus_adjacent_cell (GCAL_MONTH_VIEW_ROW (row), focused); + } } return GTK_WIDGET_CLASS (gcal_month_view_parent_class)->focus (widget, direction); -- GitLab