如何实现GtkApplication窗口仅外部失焦时退出,避免子控件误触发
解决Gtk3中点击ComboBox导致主窗口因focus-out-event退出的问题
问题原因
GtkComboBox的下拉列表是独立的顶层弹出窗口,点击ComboBox展开选项时,系统焦点会转移到这个弹出窗口,触发主窗口的focus-out-event信号,进而执行退出逻辑。而Scale是主窗口内部控件,点击它时焦点仍在主窗口控件树内,不会触发主窗口的焦点丢失事件。
解决方案
修改退出回调函数,在执行退出前判断新焦点窗口是否属于当前应用(比如ComboBox的弹出窗口)或主窗口的子控件。仅当焦点转移到应用外部时,才退出程序。
修改后的完整代码
#include <gtk/gtk.h> // gcc `pkg-config --cflags gtk+-3.0` -o example Changer3.c `pkg-config --libs gtk+-3.0` gboolean quit_function(GtkWidget *widget, GdkEvent *event, gpointer user_data) { GdkEventFocus *focus_event = (GdkEventFocus *)event; GdkWindow *new_focus_window = focus_event->focus; // 检查新焦点窗口是否属于当前应用的顶层窗口(如ComboBox弹出窗口) if (new_focus_window != NULL) { GtkWidget *new_focus_toplevel = gtk_widget_get_toplevel(gtk_widget_get_for_window(new_focus_window)); if (new_focus_toplevel != NULL && gtk_widget_is_toplevel(new_focus_toplevel)) { GtkApplication *app = GTK_APPLICATION(g_application_get_default()); GList *app_windows = gtk_application_get_windows(app); for (GList *l = app_windows; l != NULL; l = l->next) { if (l->data == new_focus_toplevel) { g_list_free(app_windows); return GDK_EVENT_PROPAGATE; // 焦点在应用内,不退出 } } g_list_free(app_windows); } // 检查新焦点窗口是否是主窗口的子控件 GtkWidget *focus_widget = gtk_widget_get_for_window(new_focus_window); if (focus_widget != NULL && gtk_widget_is_ancestor(focus_widget, widget)) { return GDK_EVENT_PROPAGATE; // 焦点在主窗口内,不退出 } } // 焦点转移到应用外部,执行退出 GApplication *app = g_application_get_default(); g_application_quit(app); return GDK_EVENT_STOP; } static void activate(GtkApplication *app, gpointer user_data) { GtkWidget *window; GtkWidget *center_vbox; GtkWidget *combobox; GtkWidget *scale; window = gtk_application_window_new(app); gtk_window_set_decorated(GTK_WINDOW(window), FALSE); gtk_window_set_default_size(GTK_WINDOW(window), 230, 100); gtk_widget_set_size_request(window, 230, 100); gtk_window_set_title(GTK_WINDOW(window), "config"); gtk_window_set_resizable(GTK_WINDOW(window), FALSE); gtk_window_set_keep_above(GTK_WINDOW(window), TRUE); center_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); gtk_container_add(GTK_CONTAINER(window), center_vbox); // 给ComboBox添加测试选项 combobox = gtk_combo_box_new_text(); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), "选项1"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), "选项2"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), "选项3"); gtk_box_pack_start(GTK_BOX(center_vbox), combobox, FALSE, FALSE, 0); scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 50, 1); gtk_box_pack_start(GTK_BOX(center_vbox), scale, TRUE, TRUE, 0); gtk_widget_set_events(window, GDK_FOCUS_CHANGE_MASK); g_signal_connect(G_OBJECT(window), "focus-out-event", G_CALLBACK(quit_function), NULL); gtk_widget_show_all(window); } int main(int argc, char **argv) { GtkApplication *app; int status; app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }
关键修改说明
- 焦点判断逻辑:在退出回调中先验证新焦点窗口的归属,确保只有焦点离开应用时才执行退出。
- 添加ComboBox测试选项:原代码无下拉选项,补充后方便验证下拉功能的焦点行为。
修改后,点击ComboBox下拉选项、Scale控件时主窗口不会关闭,仅点击应用外部区域时程序才会退出。
内容的提问来源于stack exchange,提问作者dibyendu
相关产品推荐
相关产品推荐

