Monday, August 19, 2013

(Re)discovered a bug in Gtk+ - Clickable widgets inside a GtkExpander's label-widget

GtkLabel is one of those (which those? didn't find the answer- lazyweb) widgets which do not have a window of their own; so they cannot receive events. To make a label clickable, you have to wrap it inside an GtkEventBox and receive "button-press-event" on the event-box. That's the simple part.

Now when I apply the same technique and wrap a GtkLabel which is a part of the Label widget of a GtkExpander in an event-box to make it clickable then button-press-event isn't generated! If you think there's something peculiar with labels and think you can use buttons-with-no-relief instead of labels in the label-widget of GtkExpander then no, that's not an option since even buttons receive no events inside the label-widget of a GtkExpander.

..But, according to this, the events are delivered first to the child widget then to parent widgets so the label should have received the event in the first place! It turns out to be a bug in gtk+ which was similar to the one described here.

Here's the code with a hack which solves my problem for the time-being:

#include  
GtkWidget *expander;
gboolean
hacked_idle (GtkWidget *hbox)
{
     g_object_ref (G_OBJECT (hbox));
     gtk_expander_set_label_widget (GTK_EXPANDER (expander), NULL);
     gtk_expander_set_label_widget (GTK_EXPANDER (expander), hbox);
     g_object_unref (G_OBJECT (hbox));
     return FALSE;
}

void clicked(GtkButton *button, void *which)
{
    g_debug ("%s clicked\n", which);
}
gboolean button_pressed (GtkWidget *widget,
    GdkEvent  *event,
    gpointer   user_data)
{
    g_debug ("label clicked with button %d", ((GdkEventButton *)event)->button);
}
int main(int argc, char **argv)
{
    gtk_init (&argc, &argv); // could technically do autolist_add(main_init, gtk_init); but it wouldnt buy me anything
    GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    GtkWidget *vbox1 = gtk_vbox_new (FALSE, 0);

   gtk_container_add(GTK_CONTAINER(window), vbox1); 

    expander = gtk_expander_new(NULL); 
    gtk_expander_set_expanded(GTK_EXPANDER(expander), TRUE); 
    gtk_box_pack_start(GTK_BOX(vbox1), expander, FALSE, FALSE, 0); 

    GtkWidget *hbox = gtk_hbox_new(FALSE, 0); 
    gtk_expander_set_label_widget(GTK_EXPANDER(expander), hbox); 

    GtkLabel *label = GTK_LABEL(gtk_label_new("The Label")); 
    GtkWidget *event_box = gtk_event_box_new ();
    gtk_container_add (GTK_CONTAINER (event_box), GTK_WIDGET(label));
    gtk_box_pack_start(GTK_BOX(hbox), event_box, TRUE, TRUE, 0);
    g_signal_connect (event_box, "button-press-event",
                      G_CALLBACK (button_pressed),
                      NULL);

    GtkWidget *edit_button = gtk_button_new_with_mnemonic("Edit"); 
    gtk_box_pack_start(GTK_BOX(hbox), edit_button, FALSE, FALSE, 0); 
    gtk_widget_set_sensitive(edit_button, TRUE); 
    g_signal_connect(edit_button, "clicked", 
                     G_CALLBACK(clicked), 
                     (gpointer)"Edit");

    GtkWidget *delete_button = gtk_button_new_with_mnemonic("Delete");
    gtk_box_pack_start(GTK_BOX(hbox), delete_button, FALSE, FALSE, 0);
    gtk_widget_set_sensitive(delete_button, TRUE);
    g_signal_connect(delete_button, "clicked",
                     G_CALLBACK(clicked),
                     (gpointer)"Delete");

    GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(expander), vbox);
    // ... put some junk in the vbox 
    gtk_widget_show_all(window); 

    g_idle_add ((GSourceFunc) hacked_idle, hbox);
    gtk_main(); 
    return 0;
}




I filed a bug @bugzilla for the same: https://bugzilla.gnome.org/show_bug.cgi?id=705971
Relevant bug in Empathy: https://bugzilla.gnome.org/show_bug.cgi?id=679919