mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-08 17:59:56 +00:00
[client/ui-wails] Tear down the whole tray popup tree on focus loss
Replace the per-submenu focus-out handler with a shared idle-deferred recheck: when any popup loses focus, ask after the next event-loop turn whether *any* of our popups still owns toplevel focus. If none does, the user clicked outside the menu tree, so close every popup at once instead of leaking the parent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -303,16 +303,34 @@ static void on_check_toggled(GtkToggleButton *btn, gpointer user_data) {
|
||||
goMenuItemClicked(id);
|
||||
}
|
||||
|
||||
/* When any popup loses focus we want to close the entire popup tree —
|
||||
unless focus moved to another window we own (e.g. opening a submenu).
|
||||
focus-out fires before the corresponding focus-in on the new window,
|
||||
so we defer the check to an idle callback: by then any sibling popup
|
||||
has had a chance to grab focus. If none of our windows still has
|
||||
toplevel focus, the user clicked outside the menu tree → tear down. */
|
||||
static gboolean any_popup_has_focus(void) {
|
||||
if (popup_win && gtk_window_has_toplevel_focus(GTK_WINDOW(popup_win)))
|
||||
return TRUE;
|
||||
for (GList *l = submenu_popups; l; l = l->next) {
|
||||
if (gtk_window_has_toplevel_focus(GTK_WINDOW(l->data)))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean focus_out_recheck(gpointer user_data) {
|
||||
(void)user_data;
|
||||
if (!any_popup_has_focus()) {
|
||||
close_all_popups();
|
||||
}
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean on_popup_focus_out(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer user_data) {
|
||||
(void)widget; (void)event; (void)user_data;
|
||||
/* Don't tear the menu down when the top-level loses focus to one of
|
||||
its own submenu popups. close_all_popups() would destroy the
|
||||
submenu we just opened. The submenu's own focus-out handler will
|
||||
close it (and us) when focus leaves the popup tree. */
|
||||
if (submenu_popups != NULL)
|
||||
return FALSE;
|
||||
close_all_popups();
|
||||
g_idle_add(focus_out_recheck, NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -325,16 +343,6 @@ typedef struct {
|
||||
GtkWidget *anchor; /* the submenu button — used to position the popup */
|
||||
} submenu_open_data;
|
||||
|
||||
/* Tear-down companion for submenu_open_data: detach from submenu_popups
|
||||
and destroy. Used when the focus-out handler fires on a submenu. */
|
||||
static gboolean on_submenu_focus_out(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer user_data) {
|
||||
(void)event; (void)user_data;
|
||||
submenu_popups = g_list_remove(submenu_popups, widget);
|
||||
gtk_widget_destroy(widget);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void on_submenu_button_clicked(GtkButton *btn, gpointer user_data) {
|
||||
submenu_open_data *sd = (submenu_open_data *)user_data;
|
||||
|
||||
@@ -347,7 +355,7 @@ static void on_submenu_button_clicked(GtkButton *btn, gpointer user_data) {
|
||||
gtk_window_set_keep_above(GTK_WINDOW(win), TRUE);
|
||||
|
||||
g_signal_connect(win, "focus-out-event",
|
||||
G_CALLBACK(on_submenu_focus_out), NULL);
|
||||
G_CALLBACK(on_popup_focus_out), NULL);
|
||||
|
||||
GtkWidget *vbox = build_menu_box(sd->items, sd->count);
|
||||
gtk_container_add(GTK_CONTAINER(win), vbox);
|
||||
|
||||
Reference in New Issue
Block a user