Nidhoegger
Nidhoegger

Reputation: 5232

GTK+2: Drawing above other widgets transparently

I am forced to use Gtk+2. So please no discussion about switching to 3 or 4.

I need to create something similar to Gtk3s GtkOverlay and I need to draw a transparent background over other widgets.

I thought to use GtkFixed and put my "normal" widgets there, then use a GtkEventBox to put on top and connect its expose event after setting the colormap (if supported) to transparent.

I got something working but it has not the expected result. It results in the event box beeing half-transparent (as I want it), but now showing the widgets underneath, but showing everything below the window.

Here is my code so far (sorry for the mess):

#include <gtk/gtk.h>

GtkWidget *window;
GtkWidget *button;
GtkWidget *vbox;
GtkWidget *fixed;  
GtkWidget *event_overlay;
gboolean supports_alpha = FALSE;

static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer userdata)
{
    /* To check if the display supports alpha channels, get the colormap */
    GdkScreen *screen = gtk_widget_get_screen(widget);
    GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);

    if (!colormap)
    {
        printf("Your screen does not support alpha channels!\n");
        colormap = gdk_screen_get_rgb_colormap(screen);
        supports_alpha = FALSE;
    }
    else
    {
        printf("Your screen supports alpha channels!\n");
        supports_alpha = TRUE;
    }

    gtk_widget_set_colormap(widget, colormap);
}

static gboolean exposed (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
    cairo_t *cr = gdk_cairo_create(widget->window);
 
    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5); /* transparent */

    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_paint (cr);

    cairo_destroy(cr);

    return TRUE;
}

static void hello( GtkWidget *widget,
                   gpointer   data )
{
    gtk_fixed_put(GTK_FIXED(fixed), event_overlay, 100, 100);
}

static void destroy( GtkWidget *widget,
                     gpointer   data )
{
    gtk_main_quit ();
}

int main( int   argc,
          char *argv[] )
{
  

    gtk_init (&argc, &argv);
    
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    g_signal_connect (window, "destroy",
              G_CALLBACK (destroy), NULL);
              
    button = gtk_button_new_with_label ("Hello World");
    g_signal_connect (button, "clicked",
              G_CALLBACK (hello), NULL);
              
    vbox = gtk_vbox_new(TRUE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0);
    
    fixed = gtk_fixed_new();
    gtk_fixed_set_has_window(GTK_FIXED(fixed), TRUE);
    gtk_widget_set_size_request(button, 500, 500);
    gtk_fixed_put(GTK_FIXED(fixed), vbox, 0, 0);
    event_overlay = gtk_event_box_new();
    gtk_widget_set_size_request(event_overlay, 500, 500);
    g_signal_connect(event_overlay, "expose-event", G_CALLBACK(exposed), NULL);
    g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(screen_changed), NULL);
    gtk_widget_set_app_paintable(window, TRUE);


    gtk_container_add (GTK_CONTAINER (window), fixed);
    screen_changed(window, NULL, NULL);
    gtk_widget_show_all (window);
    gtk_widget_show(event_overlay);
    
    gtk_main ();
    
    return 0;
}

And here is the result: Transparency done wrong

Upvotes: 5

Views: 524

Answers (1)

dumbass
dumbass

Reputation: 27228

It turns out GTK+ 2 authors have actually foreseen this use case. There is a pair of pretty similar ready-made examples included as part of the gtk-demo program, distributed with GTK+ 2 documentation. Both demos are found under the ‘Offscreen windows’ node.

The code is a bit involved, so I am not including it here; the demo actually subclasses GtkContainer to create a new widget class that forwards events to its child and renders it to an off-screen window to perform further transformations on the rendering result. But you should find the ‘Effects’ example pretty easy to adapt to your needs: all you have to do is modify gtk_mirror_bin_expose to simply paint the contents of the off-screen window into the container’s real window and then draw your semi-transparent overlay on top of that; and also adjust gtk_mirror_bin_size_request and gtk_mirror_bin_size_allocate to simply forward all sizing-related signals to the child transparently.

Upvotes: 3

Related Questions