Search code examples
drag-and-dropgtkiconscompositionalpha-transparency

Can I use icons with RGBA transparency when using GTK+ drag and drop?


I am in the process of adding drag and drop support to an existing Mono/C#/GTK# application. I was wondering whether it was possible to use RGBA transparency on the icons that appear under the mouse pointer when I start dragging an object.

So far, I realized the following:

  • I can set the bitmap in question by calling the Gtk.Drag.SourceSetIconPixbuf() method. However, no luck with alpha transparency: pixels that are not fully opaque would get 100% transparent this way.
  • I also tried calling RenderPixmapAndMask() on the GdkPixbuf so that I could use Gtk.Drag.SourceSetIcon() with an RGBA colormap of my Screen. It didn't work either: whenever I started dragging, I got the following error:

    [Gdk] IA__gdk_window_set_back_pixmap: assertion 'pixmap == NULL || gdk_drawable_get_depth (window) == gdk_drawable_get_depth (pixmap)' failed.

    This way, the pixmap doesn't even get copied, only a white shape (presumably set by the mask argument of SetSourceIcon()) shows up on dragging.

I'd like to ask if there's a way to make these icons have alpha transparency, despite the fact that I failed to do so. In case it's impossible, answers discussing the reasons of the lack of this feature would also be helpful. Thank you.

(Compositing is - of course - enabled on my desktop (Ubuntu/10.10, Compiz/0.8.6-0ubuntu9).)


Solution

  • Ok, finally I solved it. You should create a new Gtk.Window of POPUP type, set its Colormap to your screen's RGBA colormap, have the background erased by Cairo to a transparent color, draw whatever you'd like on it and finally pass it on to Gtk.Drag.SetIconWidget().

    Sample code (presumably you'll want to use this inside OnDragBegin, or at a point where you have a valid drag context to be passed to SetIconWidget()):

    Gtk.Window window = new Gtk.Window (Gtk.WindowType.Popup);
    
    window.Colormap = window.Screen.RgbaColormap;
    window.AppPaintable = true;
    window.Decorated = false;
    window.Resize (/* specify width, height */);
    
    /* The cairo context can only be created when the window is being drawn by the
     * window manager, so wrap drawing code into an ExposeEvent delegate. */
    window.ExposeEvent += delegate {
    
        Context ctx = Gdk.CairoHelper.Create (window.GdkWindow);
    
        /* Erase the background */
        ctx.SetSourceRGBA (0, 0, 0, 0);
        ctx.Operator = Operator.Source;
        ctx.Paint ();
    
        /* Draw whatever you'd like to here, and then clean up by calling
           Dispose() on the context's target. */
    
        (ctx.Target as IDisposable).Dispose ();
    };
    
    Gtk.Drag.SetIconWidget(drag_context, window, 10, 10);