Skip to content

Commit f1ed841

Browse files
mattnclaude
authored andcommitted
patch 9.2.0588: GTK4: drawing area loses focus after closing a menubar popover
Problem: After a menubar popover (e.g. File, Edit) was opened and then dismissed without selecting an item, keyboard focus remained outside the drawing area, leaving the cursor stuck in the unfocused (outline) shape until the pointer was moved over the drawarea (Foxe Chen) Solution: Install an emission hook on GtkPopover::closed and, when a popover that descends from gui.menubar closes, queue an idle callback that grabs focus back to the drawing area. The grab must be deferred because GTK is still completing the close transition when the signal fires (Yasuhiro Matsumoto). fixes: #20274 closes: #20291 Co-Authored-by: Claude <noreply@anthropic.com> Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent 3dc1ece commit f1ed841

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

src/gui_gtk4.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ static void leave_notify_event(GtkEventControllerMotion *controller, gpointer da
275275
static gboolean scroll_event(GtkEventControllerScroll *controller, double dx, double dy, gpointer data);
276276
static void focus_in_event(GtkEventControllerFocus *controller, gpointer data);
277277
static void focus_out_event(GtkEventControllerFocus *controller, gpointer data);
278+
#ifdef FEAT_MENU
279+
static gboolean menubar_popover_closed_hook(GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, gpointer data);
280+
#endif
278281
#ifdef FEAT_DND
279282
static gboolean drop_cb(GtkDropTarget *target, const GValue *value, double x, double y, gpointer data);
280283
#endif
@@ -476,6 +479,20 @@ gui_mch_init(void)
476479
gtk_widget_set_visible(gui.menubar, FALSE);
477480
gtk_box_append(GTK_BOX(vbox), gui.menubar);
478481
}
482+
// Return keyboard focus to the drawing area when a menubar popover
483+
// closes (issue #20274). GtkPopoverMenuBar owns its popovers
484+
// privately, so attach via an emission hook on GtkPopover::closed
485+
// and filter for popovers under our menubar inside the callback.
486+
{
487+
GTypeClass *cls = g_type_class_ref(GTK_TYPE_POPOVER);
488+
guint sig_id = g_signal_lookup("closed", GTK_TYPE_POPOVER);
489+
490+
if (sig_id != 0)
491+
g_signal_add_emission_hook(sig_id, 0,
492+
menubar_popover_closed_hook, NULL, NULL);
493+
if (cls != NULL)
494+
g_type_class_unref(cls);
495+
}
479496
#endif
480497

481498
#ifdef FEAT_TOOLBAR
@@ -1924,6 +1941,48 @@ focus_out_event(GtkEventControllerFocus *controller UNUSED,
19241941
gui_mch_stop_blink(TRUE);
19251942
}
19261943

1944+
#ifdef FEAT_MENU
1945+
static gboolean
1946+
grab_drawarea_focus_idle(gpointer data UNUSED)
1947+
{
1948+
if (gui.drawarea != NULL && !gtk_widget_has_focus(gui.drawarea))
1949+
gtk_widget_grab_focus(gui.drawarea);
1950+
return G_SOURCE_REMOVE;
1951+
}
1952+
1953+
static gboolean
1954+
menubar_popover_closed_hook(GSignalInvocationHint *ihint UNUSED,
1955+
guint n_param_values, const GValue *param_values,
1956+
gpointer data UNUSED)
1957+
{
1958+
GObject *obj;
1959+
GtkWidget *popover;
1960+
GtkWidget *parent;
1961+
1962+
if (n_param_values < 1 || gui.menubar == NULL || gui.drawarea == NULL)
1963+
return TRUE;
1964+
obj = g_value_get_object(&param_values[0]);
1965+
if (!GTK_IS_POPOVER(obj))
1966+
return TRUE;
1967+
popover = GTK_WIDGET(obj);
1968+
1969+
// Only react to popovers that descend from the menubar.
1970+
for (parent = gtk_widget_get_parent(popover);
1971+
parent != NULL;
1972+
parent = gtk_widget_get_parent(parent))
1973+
{
1974+
if (parent != gui.menubar)
1975+
continue;
1976+
// Defer the grab to the next main loop iteration; calling it
1977+
// synchronously while GTK is still completing the popover close
1978+
// has no effect (issue #20274).
1979+
g_idle_add(grab_drawarea_focus_idle, NULL);
1980+
break;
1981+
}
1982+
return TRUE; // keep the emission hook installed
1983+
}
1984+
#endif
1985+
19271986
static void
19281987
drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
19291988
{

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,8 @@ static char *(features[]) =
729729

730730
static int included_patches[] =
731731
{ /* Add new patch number below this line */
732+
/**/
733+
588,
732734
/**/
733735
587,
734736
/**/

0 commit comments

Comments
 (0)