diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 083ac9f4001f9..14c7f7c538d89 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -282,6 +282,9 @@ static void leave_notify_event(GtkEventControllerMotion *controller, gpointer da static gboolean scroll_event(GtkEventControllerScroll *controller, double dx, double dy, gpointer data); static void focus_in_event(GtkEventControllerFocus *controller, gpointer data); static void focus_out_event(GtkEventControllerFocus *controller, gpointer data); +#ifdef FEAT_MENU +static gboolean menubar_popover_closed_hook(GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, gpointer data); +#endif #ifdef FEAT_DND static gboolean drop_cb(GtkDropTarget *target, const GValue *value, double x, double y, gpointer data); #endif @@ -472,6 +475,20 @@ gui_mch_init(void) gtk_widget_set_visible(gui.menubar, FALSE); gtk_box_append(GTK_BOX(vbox), gui.menubar); } + // Return keyboard focus to the drawing area when a menubar popover + // closes (issue #20274). GtkPopoverMenuBar owns its popovers + // privately, so attach via an emission hook on GtkPopover::closed + // and filter for popovers under our menubar inside the callback. + { + GTypeClass *cls = g_type_class_ref(GTK_TYPE_POPOVER); + guint sig_id = g_signal_lookup("closed", GTK_TYPE_POPOVER); + + if (sig_id != 0) + g_signal_add_emission_hook(sig_id, 0, + menubar_popover_closed_hook, NULL, NULL); + if (cls != NULL) + g_type_class_unref(cls); + } #endif #ifdef FEAT_TOOLBAR @@ -1841,6 +1858,48 @@ focus_out_event(GtkEventControllerFocus *controller UNUSED, gui_mch_stop_blink(TRUE); } +#ifdef FEAT_MENU + static gboolean +grab_drawarea_focus_idle(gpointer data UNUSED) +{ + if (gui.drawarea != NULL && !gtk_widget_has_focus(gui.drawarea)) + gtk_widget_grab_focus(gui.drawarea); + return G_SOURCE_REMOVE; +} + + static gboolean +menubar_popover_closed_hook(GSignalInvocationHint *ihint UNUSED, + guint n_param_values, const GValue *param_values, + gpointer data UNUSED) +{ + GObject *obj; + GtkWidget *popover; + GtkWidget *parent; + + if (n_param_values < 1 || gui.menubar == NULL || gui.drawarea == NULL) + return TRUE; + obj = g_value_get_object(¶m_values[0]); + if (!GTK_IS_POPOVER(obj)) + return TRUE; + popover = GTK_WIDGET(obj); + + // Only react to popovers that descend from the menubar. + for (parent = gtk_widget_get_parent(popover); + parent != NULL; + parent = gtk_widget_get_parent(parent)) + { + if (parent != gui.menubar) + continue; + // Defer the grab to the next main loop iteration; calling it + // synchronously while GTK is still completing the popover close + // has no effect (issue #20274). + g_idle_add(grab_drawarea_focus_idle, NULL); + break; + } + return TRUE; // keep the emission hook installed +} +#endif + static void drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) {