Как скрыть всплывающее окно Gtk, когда пользователь щелкает за пределами окна

Я разработал одно всплывающее окно (без оформления), используя GTK+ и инструмент Glade в C. Оно всплывает в своем родительском окне при нажатии кнопки. Я хочу уничтожить или скрыть это всплывающее окно, когда пользователь щелкает за пределами этого окна. Пользователь может щелкнуть родительское окно или любое другое окно. Я пытался зафиксировать событие GDK_FOCUS_CHANGE, но не могу зафиксировать это событие. Есть ли способ добиться этого? Как я узнаю, что щелчок находится в другом окне, а затем появляется всплывающее окно? Как понять, что всплывающее окно потеряло фокус? Чтобы я мог это скрыть. Соответствующий код выглядит следующим образом:

/*
 * Compile me with:

 gcc -o popup popup.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-2.0)
*/

#include <gtk/gtk.h>

static void on_popup_clicked (GtkButton*, GtkWidget*);
static gboolean on_popup_window_event(GtkWidget*, GdkEventExpose*);

int main (int argc, char *argv[])
{
    GtkWidget *window, *button, *vbox;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Parent window");
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    gtk_widget_set_size_request (window, 300, 300);
    gtk_window_set_position (GTK_WINDOW (window),GTK_WIN_POS_CENTER);

    button = gtk_button_new_with_label("Pop Up");
    g_signal_connect (G_OBJECT (button), "clicked",G_CALLBACK (on_popup_clicked),(gpointer) window);

    vbox = gtk_vbox_new (FALSE, 3);
    gtk_box_pack_end(GTK_BOX (vbox), button, FALSE, FALSE, 5);
    gtk_container_add (GTK_CONTAINER (window), vbox);

    gtk_widget_show_all (window);
    gtk_main ();
    return 0;
}

void on_popup_clicked (GtkButton* button, GtkWidget* pWindow)
{
    GtkWidget *popup_window;
    popup_window = gtk_window_new (GTK_WINDOW_POPUP);
    gtk_window_set_title (GTK_WINDOW (popup_window), "Pop Up window");
    gtk_container_set_border_width (GTK_CONTAINER (popup_window), 10);
    gtk_window_set_resizable(GTK_WINDOW (popup_window), FALSE);
    gtk_window_set_decorated(GTK_WINDOW (popup_window), FALSE);
    gtk_widget_set_size_request (popup_window, 150, 150);
    gtk_window_set_transient_for(GTK_WINDOW (popup_window),GTK_WINDOW (pWindow));
    gtk_window_set_position (GTK_WINDOW (popup_window),GTK_WIN_POS_CENTER);
    g_signal_connect (G_OBJECT (button), "event",
                        G_CALLBACK (on_popup_window_event),NULL);

    GdkColor color;
    gdk_color_parse("#3b3131", &color);
    gtk_widget_modify_bg(GTK_WIDGET(popup_window), GTK_STATE_NORMAL, &color);


    gtk_widget_show_all (popup_window);
}

gboolean on_popup_window_event(GtkWidget *popup_window, GdkEventExpose *event)
{
    if(event->type == GDK_FOCUS_CHANGE)
        gtk_widget_hide (popup_window);

    return FALSE;
}

Здесь я не могу скрыть это всплывающее окно, когда пользователь нажимает на родительское окно или другое окно. Как я могу это сделать?

Я должен придерживаться версии Gtk+2.14.


c gtk
person kbalar    schedule 17.11.2009    source источник
comment
Пожалуйста, не репостите вопросы. Похоже, это (лучше представленное) повторение stackoverflow.com/questions/1740947/   -  person mlibby    schedule 17.11.2009
comment
Я позабочусь об этом в будущем.   -  person kbalar    schedule 17.11.2009


Ответы (3)


Изменения:

  • переключиться с GTK_WINDOW_POPUP на GTK_WINDOW_TOPLEVEL, нелогично, но я не мог понять, как заставить всплывающее окно принять фокус.
  • добавить gtk_window подсказки, чтобы всплывающее окно не отображалось на панели задач и на пейджере
  • намеренно установить фокус на всплывающем окне
  • установите GDK_FOCUS_CHANGE_MASK на GDK_WINDOW с помощью gtk_widget_set_events (требуется для следующего шага)
  • подключиться к focus-out-event всплывающего окна
  • изменить обработчик сигнала для обработки другого сигнала

Я бы также посоветовал прочитать исходный код GTK+, чтобы увидеть, как он обрабатывает всплывающие окна для всплывающих подсказок и меню, когда они отображаются... но они обычно уничтожаются из-за перемещения мыши за пределы диапазона, а не из-за того, что всплывающее окно теряет фокус, как таковое.


#include 

static void on_popup_clicked (GtkButton*, GtkWidget*);
gboolean on_popup_focus_out (GtkWidget*, GdkEventFocus*, gpointer);

int
main (int argc, char *argv[])
{
  GtkWidget *window, *button, *vbox;

  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Parent window");
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  gtk_widget_set_size_request (window, 300, 300);
  gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);

  button = gtk_button_new_with_label ("Pop Up");
  g_signal_connect (G_OBJECT (button),
                    "clicked",
                    G_CALLBACK (on_popup_clicked),
                    (gpointer) window);

  vbox = gtk_vbox_new (FALSE, 3);
  gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 5);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  gtk_widget_show_all (window);
  gtk_main ();
  return 0;
}

void
on_popup_clicked (GtkButton* button, GtkWidget* pWindow)
{
  GtkWidget *popup_window;

  popup_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (popup_window), "Pop Up window");
  gtk_container_set_border_width (GTK_CONTAINER (popup_window), 10);
  gtk_window_set_resizable (GTK_WINDOW (popup_window), FALSE);
  gtk_window_set_decorated (GTK_WINDOW (popup_window), FALSE);
  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (popup_window), TRUE);
  gtk_window_set_skip_pager_hint (GTK_WINDOW (popup_window), TRUE);
  gtk_widget_set_size_request (popup_window, 150, 150);
  gtk_window_set_transient_for (GTK_WINDOW (popup_window), GTK_WINDOW (pWindow));
  gtk_window_set_position (GTK_WINDOW (popup_window), GTK_WIN_POS_CENTER);

  gtk_widget_set_events (popup_window, GDK_FOCUS_CHANGE_MASK);
  g_signal_connect (G_OBJECT (popup_window),
                    "focus-out-event",
                    G_CALLBACK (on_popup_focus_out),
                    NULL);

  GdkColor color;
  gdk_color_parse ("#3b3131", &color);
  gtk_widget_modify_bg (GTK_WIDGET (popup_window), GTK_STATE_NORMAL, &color);

  gtk_widget_show_all (popup_window);
  gtk_widget_grab_focus (popup_window);
}

gboolean
on_popup_focus_out (GtkWidget *widget,
                    GdkEventFocus *event,
                    gpointer data)
{
  gtk_widget_destroy (widget);
  return TRUE;
}
person mlibby    schedule 17.11.2009
comment
Хорошая сделка. Если этот ответ работает для вас, не стесняйтесь щелкать большую галочку рядом с ответом. :) - person mlibby; 17.11.2009
comment
Отличное решение, но есть одна проблема: главное окно визуально регистрирует изменения фокуса, окрашивая границу по-разному (Windows 7). Этого не должно происходить для (поддельного) всплывающего окна. Любая идея предотвратить это? - person schlamar; 12.10.2012
comment
Чтобы было понятно, вот несколько скриншотов: imageshack.us/g/211/beforegp.png (взгляните, например, на значки) - person schlamar; 12.10.2012

Вам не нужно устанавливать фокус клавиатуры на всплывающее окно.

Вам просто нужно захватить мышь на свой popup_window->window, используя gdk_pointer_grab(...) с аргументами True owner_events и GDK_BUTTON_PRESS_MASK GdkEventMask.

Затем подключите popup_window к "button-press-event". Внутри его обработчика скройте/уничтожьте ваше всплывающее_окно и освободите захват, используя gdk_pointer_ungrab(...), если *координаты события отрицательны или превышают размер вашего всплывающего_окна.

person Brylev Daniel    schedule 10.09.2010

Другой альтернативой является простое добавление прослушивателя нажатия кнопки в родительское окно. Это имеет то преимущество, что всплывающее окно по-прежнему выглядит как всплывающее окно (и родитель, и само по себе могут быть активны одновременно).

#include <stdio.h>
#include <gtk/gtk.h>

static void on_popup_clicked (GtkButton*, GtkWidget*);

gulong handler_id;

gboolean
on_click (GtkWidget *widget,
               GdkEvent  *event,
               gpointer   user_data)
{
  g_signal_handler_disconnect (widget, handler_id);
  gtk_widget_destroy (user_data);
  return TRUE;
}


gboolean
on_popup_focus_out (GtkWidget *widget,
                    GdkEventFocus *event,
                    gpointer data)
{
  gtk_widget_destroy (widget);
  return TRUE;
}


int
main (int argc, char *argv[])
{
  GtkWidget *window, *button, *vbox;

  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Parent window");
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  gtk_widget_set_size_request (window, 300, 300);
  gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);

  button = gtk_button_new_with_label ("Pop Up");
  g_signal_connect (G_OBJECT (button),
                    "clicked",
                    G_CALLBACK (on_popup_clicked),
                    (gpointer) window);

  vbox = gtk_vbox_new (FALSE, 3);
  gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 5);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  gtk_widget_show_all (window);
  gtk_main ();
  return 0;
}

void
on_popup_clicked (GtkButton* button, GtkWidget* pWindow)
{
  GtkWidget *popup_window;

  popup_window = gtk_window_new (GTK_WINDOW_POPUP);
  gtk_window_set_title (GTK_WINDOW (popup_window), "Pop Up window");
  gtk_container_set_border_width (GTK_CONTAINER (popup_window), 10);
  gtk_window_set_resizable (GTK_WINDOW (popup_window), FALSE);
  gtk_window_set_decorated (GTK_WINDOW (popup_window), FALSE);
  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (popup_window), TRUE);
  gtk_window_set_skip_pager_hint (GTK_WINDOW (popup_window), TRUE);
  gtk_widget_set_size_request (popup_window, 150, 150);
  gtk_window_set_transient_for (GTK_WINDOW (popup_window), GTK_WINDOW (pWindow));
  gtk_window_set_position (GTK_WINDOW (popup_window), GTK_WIN_POS_CENTER);

  gtk_widget_add_events (popup_window, GDK_FOCUS_CHANGE_MASK);
  gtk_widget_add_events (pWindow, GDK_BUTTON_PRESS_MASK);

  g_signal_connect (G_OBJECT (popup_window),
                    "focus-out-event",
                    G_CALLBACK (on_popup_focus_out),
                    NULL);

  handler_id = g_signal_connect (G_OBJECT (pWindow),
                    "button-press-event",
                    G_CALLBACK (on_click),
                    popup_window);

  GdkColor color;
  gdk_color_parse ("#3b3131", &color);
  gtk_widget_modify_bg (GTK_WIDGET (popup_window), GTK_STATE_NORMAL, &color);

  gtk_widget_show_all (popup_window);
  gtk_widget_grab_focus (popup_window);
}
person simonzack    schedule 01.11.2015