You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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;
}

关键修改说明

  1. 焦点判断逻辑:在退出回调中先验证新焦点窗口的归属,确保只有焦点离开应用时才执行退出。
  2. 添加ComboBox测试选项:原代码无下拉选项,补充后方便验证下拉功能的焦点行为。

修改后,点击ComboBox下拉选项、Scale控件时主窗口不会关闭,仅点击应用外部区域时程序才会退出。

内容的提问来源于stack exchange,提问作者dibyendu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:24:53