I am trying to create a GUI in GTK using C. I want to have radio buttons but I don't know how to determine which one is selected.
#include <glib.h>
#include <gtk/gtk.h>
static gboolean close_application( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
gtk_main_quit ();
return FALSE;
}
static void btn_selected()
{
//pop-up message; left empty for now
}
int main( int argc,
char *argv[] )
{
GtkWidget *window = NULL;
GtkWidget *box1;
GtkWidget *box2;
GtkWidget *button;
GtkWidget *separator;
GSList *group;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete-event",
G_CALLBACK (close_application),
NULL);
gtk_window_set_title (GTK_WINDOW (window), "radio buttons");
gtk_container_set_border_width (GTK_CONTAINER (window), 0);
box1 = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (window), box1);
gtk_widget_show (box1);
box2 = gtk_vbox_new (FALSE, 10);
gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
gtk_widget_show (box2);
button = gtk_radio_button_new_with_label (NULL, "button1");
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_show (button);
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
button = gtk_radio_button_new_with_label (group, "button2");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_show (button);
button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (button),
"button3");
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_show (button);
separator = gtk_hseparator_new ();
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
gtk_widget_show (separator);
box2 = gtk_vbox_new (FALSE, 10);
gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
gtk_widget_show (box2);
button = gtk_button_new_with_label ("close");
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (close_application),
window);
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_set_can_default (button, TRUE);
gtk_widget_grab_default (button);
gtk_widget_show (button);
//This button will call the btn_selected method when clicked
button = gtk_button_new_with_label("selected");
g_signal_connect(button,"clicked",G_CALLBACK (btn_selected),NULL);
gtk_box_pack_start(GTK_BOX(box2),button,TRUE,TRUE,0);
gtk_widget_show(button);
gtk_widget_show (window);
gtk_main ();
return 0;
}
The GUI comes up without any errors, I just don't know to check which radio button was selected so I can perform an action. Since all the buttons are named button I don't know how to check for a specific one.I got this code from a gtk tutorial online.
Any ideas would be greatly appreciated.
GtkRadioButton is descended from GtkToggleButton so you can connect to the 'toggled' signal or use the 'active' property to the the state of the button.
Related
I have an App which as some point Opens a new Window and works fine.
Then After I'm done with it, I need to switch back to the main window.
I understood that I need the function:
gtk_widget_hide();
but I cant figure out how to hide the main Window, to print only the second one and again after I click the button in the second window to go back to the first one.
This is what I have so far:
#include <stdio.h>
#include <gtk/gtk.h>
static void crete_new_wind (GtkWidget *widget);
gboolean destroy (GtkWidget *window);
int main (int argc, char *argv[]){
GtkWidget *window, *button, *box;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "First 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 ("Go to Window B");
g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (crete_new_wind), (gpointer) window);
box = gtk_box_new (TRUE, 1);
gtk_box_pack_end (GTK_BOX (box), button, TRUE, TRUE, 1);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
void crete_new_wind (GtkWidget *widget){
GtkWidget *window, *button, *box;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Second 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);;
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), widget);
button = gtk_button_new_with_label ("Go back to Window A");
g_signal_connect (G_OBJECT (button), "destroy", G_CALLBACK (destroy), NULL);
gtk_widget_hide(widget);
box = gtk_box_new (TRUE, 1);
gtk_box_pack_end (GTK_BOX (box), button, TRUE, TRUE, 1);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_widget_show_all (window);
}
gboolean destroy (GtkWidget *widget){
gtk_widget_destroy (widget);
return TRUE;
}
If I click on the button(Go to Window B) I have this:
But the main window is still there, avaible to the user which is not what I need.
Thats because the clicked callback wants two parameters
Change the prototype and the function to
static void crete_new_wind(GtkButton *dummy, gpointer widget);
static void crete_new_wind(GtkButton *dummy, gpointer widget) {
in order to use the second parameter in your function.
I want to set the position of the window using gtk_window_set_position
but it seems that after the window is created, the gtk_window_set_position will not take effect.
I'm wondering how to call gtk_window_set_position after the window shows up, i.e. a button event?
I just wrote a minimal working example that implements two approaches. One approach uses gtk_window_move and the other gtk_window_set_position.
If you have the gtk+ reference at hand the code should be self explanatory.
#include <gtk/gtk.h>
#include <glib.h>
void
button1_clicked_cb (GtkWidget * widget, GtkWindow * window)
{
GdkWindow *root;
gint width, height, rwidth, rheight;
gtk_window_get_size (window, &width, &height);
root = gtk_widget_get_root_window (GTK_WIDGET (window));
gdk_window_get_geometry (root, NULL, NULL, &rwidth,
&rheight);
gtk_window_move (window, (rwidth - width) / 2,
(rheight - height) / 2);
}
void
button2_clicked_cb (GtkWidget * widget, GtkWindow * window)
{
gtk_widget_hide (GTK_WIDGET (window));
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show_all (GTK_WIDGET (window));
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *button1;
GtkWidget *button2;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
button1 = gtk_button_new_with_label ("approach 1");
button2 = gtk_button_new_with_label ("approach 2");
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
gtk_box_pack_start (GTK_BOX (box), button1, TRUE, TRUE,
10);
gtk_box_pack_start (GTK_BOX (box), button2, TRUE, TRUE,
10);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_widget_show_all (window);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (button1, "clicked",
G_CALLBACK (button1_clicked_cb),
window);
g_signal_connect (button2, "clicked",
G_CALLBACK (button2_clicked_cb),
window);
gtk_main ();
return 0;
}
// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
g_cancellable_cancel (can1);
can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
struct tst *prg = (gpointer)ptr1;
g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
if (err1 != NULL) g_error_free (err1);
gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->hb = gtk_hbox_new (FALSE, 0);
prg->pbar = gtk_progress_bar_new ();
prg->b2 = gtk_button_new_with_label ("Cancel");
gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
gtk_widget_show_all (GTK_WIDGET (prg->w2));
can1 = g_cancellable_new ();
err1 = NULL;
t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
}
static void window_new ()
{
struct tst *prg = g_new0 (struct tst, 1);
prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->vb = gtk_vbox_new (FALSE, 0);
prg->btn = gtk_button_new_with_label ("start");
gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
gtk_widget_show_all (GTK_WIDGET (prg->win));
gtk_main ();
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
window_new ();
return 0;
}
This program is an example. When 'start' button is clicked the program creates a window with progress bar and 'cancel' button, and make a thread to copy /1.avi to /2.avi, but because of /1.avi doesn't exist the program will write 'Error!' on terminal and close the progress bar window.
But there is one problem in this program. While I click 'start' button many times somewhen the program writes another error messages on terminal. Sometimes the messages are about GDK, sometimes about GObject, and sometimes about GTK+. And sometimes program itself is freezed or crashed.
// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
g_cancellable_cancel (can1);
can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
struct tst *prg = (gpointer)ptr1;
g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
if (err1 != NULL) g_error_free (err1);
gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->hb = gtk_hbox_new (FALSE, 0);
prg->pbar = gtk_progress_bar_new ();
prg->b2 = gtk_button_new_with_label ("Cancel");
gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
gtk_widget_show_all (GTK_WIDGET (prg->w2));
can1 = g_cancellable_new ();
err1 = NULL;
t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
g_thread_join (t1);
}
static void window_new ()
{
struct tst *prg = g_new0 (struct tst, 1);
prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->vb = gtk_vbox_new (FALSE, 0);
prg->btn = gtk_button_new_with_label ("start");
gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
gtk_widget_show_all (GTK_WIDGET (prg->win));
gtk_main ();
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
window_new ();
return 0;
}
So I modified some parts of program. Now the problem not occurs though I click 'start' button many times, but I met another problem. When I can copy /1.avi to /2.avi, if I click 'start' button then program copies /1.avi to /2.avi but during this process the program window freezes, and progress bar window doesn't appear(On first example this problem isn't occured).
What should I do to make this program to meet neither of two problems?
The immediate problem I can see with your code is that you are calling a Gtk function in t1_do which is run from the secondary thread without enclosing it in gtk_thread_enter (); gtk_thread_leave ();
The general rule is that you should never call a UI function from a thread that isn't the main UI thread. Until you fix that problem you could just get completely random errors and behaviour
I need to output the i progress bars and update them all. But only the last one updates i times. This is the code:
static void calculaPi (GtkButton * boton, Datos * dDatos){
const char * threads;
GtkWidget * barra, *bot2, *button, *progress, *vbox;
threads = gtk_entry_get_text(GTK_ENTRY(dDatos->dthreads ));
gint ithreads = 1;
ithreads = atoi(threads);
barra = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title((GtkWindow *) barra, "Loteria de Threads");
gtk_window_set_default_size(GTK_WINDOW(barra), 300, ithreads*30);
gtk_window_set_position(GTK_WINDOW(barra), GTK_WIN_POS_CENTER);
button = gtk_button_new_with_label ("Click me!");
vbox = gtk_vbox_new (FALSE, 5);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 5);
gtk_container_add (GTK_CONTAINER (barra), vbox);
for (gint i = 1 ; i <= ithreads; i++) {
progress = gtk_progress_bar_new ();
gtk_box_pack_start (GTK_BOX (vbox), progress, FALSE, FALSE, 5);
g_object_set_data (G_OBJECT (barra), "pbar", (gpointer) progress);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (button_clicked), (gpointer) barra);
}
bot2 = gtk_button_new_with_label("Salir");
gtk_box_pack_start (GTK_BOX (vbox), bot2, FALSE, FALSE, 5);
gtk_widget_set_size_request(bot2, 100, 35);
g_signal_connect (G_OBJECT (bot2), "clicked",
G_CALLBACK (destroy),
G_OBJECT (barra));
gtk_widget_show_all(barra);
gtk_main();
}
static void
button_clicked (GtkButton *button,
GtkWidget *barra)
{
GtkProgressBar *progress;
gdouble percent = 0.0;
gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
progress = GTK_PROGRESS_BAR (g_object_get_data (G_OBJECT (barra), "pbar"));
while (percent <= 100.0)
{
gchar *message = g_strdup_printf ("%.0f%% Complete", percent);
gtk_progress_bar_set_fraction (progress, percent / 100.0);
gtk_progress_bar_set_text (progress, message);
while (gtk_events_pending ())
gtk_main_iteration ();
g_usleep (500000);
percent += 5.0;
}
}
With this line:
g_object_set_data (G_OBJECT (barra), "pbar", (gpointer) progress);
you override previous value of "pbar" data entry on each loop iteration. When you later retrive it in button_clicked() you get the last set value, i.e. the last progress bar.
In this particular case you can just pass progress widget as user data (and drop g_object_[gs]et_data() calls) for button_clicked() callback: the function doesn't use current barra window for anything else anyway.
In a more general way, you should learn how to use your own structures for user data parameters. A common way is to declare and use one structure for given toplevel type and store pointers to widgets you need to access from callbacks in it.
I have developed one popup window (Non decorated) using GTK+ and glade tool in C.
It popup on its parent window when a button clicked. I want to destroy or hide this popup window when user clicks out side this window. User can click on parent window or any other window.
I have tried to capture GDK_FOCUS_CHANGE event but I am not able to capture this event. Is there any way to achieve this? How do I know that click is on other window then pop up window? How is it clear that pop up window has lost it's focus?
So that I can hide it.
The relevant code is as follow:
/*
* 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;
}
Here I am not able to hide this pop up window when user clicks on parent window or on other window. How can I do this?
I have to stick with Gtk+2.14 version.
Changes:
switch from GTK_WINDOW_POPUP to GTK_WINDOW_TOPLEVEL, counter-intuitive, but I could not figure out how to get a popup to accept focus.
add gtk_window hints to prevent popup from showing in taskbar and pager
intentionally set the focus on the popup window
set the GDK_FOCUS_CHANGE_MASK on the GDK_WINDOW with gtk_widget_set_events (required for the next step)
connect to the focus-out-event of the popup window
change the signal handler to handle a different signal
I would also suggest reading the GTK+ source to see how it handles popup windows for tooltips and menus when they are shown... but those are usually destroyed based on the mouse moving out of range, not the popup losing focus, per se.
#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;
}
You don't need to set keyboard focus to your popup window.
You just need to capture mouse to your popup_window->window using gdk_pointer_grab(...) with True owner_events and GDK_BUTTON_PRESS_MASK GdkEventMask arguments.
Then connect your popup_window to "button-press-event". Inside it's handler hide/destroy your popup_window and release capture using gdk_pointer_ungrab(...) if *event coordinates are negative or higher than your popup_window size.
Another alternative is to simply add a button press listener to the parent window. This has the advantage that the popup window still looks like a popup (both the parent and itself can be active at once)
#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);
}