我如何真正制作GTK + 3 GtkLayout透明/绘制主题背景?

时间:2014-04-08 14:49:12

标签: gtk opacity gtk3

如果我创建一个内部没有任何内容的普通GtkWindow或者内部带有GtkDrawingArea的GtkWindow(或者在GtkScrolledWindow中),我会得到一个主题背景的窗口,如this picture中所示。

但如果我使用GtkLayout,我会得到一个带有纯色背景的窗口,如this picture中所示。

到目前为止,任何人(包括我自己)都可以找到的唯一资源是this other Stack Overflow question,但是当我按照它所说的去做时,我会得到一个全黑的背景,就像this picture一样,即使我获取GtkLayout的bin窗口的cairo上下文。

那么如何才能让GtkLayout变得透明,或者如果这不是一个选项,那么使用主题的背景呢?

虽然我使用GTK + 3.10运行,但我需要定位GTK + 3.4,因此gtk_widget_set_opacity()将无效。我的截图中显示的主题是gtk-oxygen主题;我正在使用KDE。

以下示例程序。它可以选择涵盖我上面描述的所有情况。我还尝试了draw()中各种cairo调用的变体(注释了一些,在设置CLEAR后添加了另一个alpha颜色选择,没有设置剪辑矩形等);这也没用。

谢谢!

/* pietro gagliardi - 7-8 april 2014 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4
#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4
#include <gtk/gtk.h>

gboolean windowonly = FALSE;
gboolean drawingarea = FALSE;
gboolean viewport = FALSE;
gboolean drawsig = FALSE;
gboolean binwin = FALSE;

void parseargs(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
        goto usage;

#define extra(str) (argc == 3 && strcmp(argv[2], str) == 0)
#define noextra(cond) if (!(cond) && argc == 3) goto usage;
    if (strcmp(argv[1], "windowOnly") == 0) {
        noextra(FALSE);
        windowonly = TRUE;
        return;
    } else if (strcmp(argv[1], "drawingArea") == 0) {
        drawingarea = TRUE;
        viewport = extra("viewport");
        noextra(viewport);
        return;
    } else if (strcmp(argv[1], "layout") == 0) {
        binwin = extra("drawbin");
        drawsig = binwin || extra("draw");
        noextra(drawsig);
        return;
    }

usage:
    fprintf(stderr, "usage:\n");
    fprintf(stderr, "\t%s windowOnly\n", argv[0]);
    fprintf(stderr, "\t%s drawingArea [viewport]\n", argv[0]);
    fprintf(stderr, "\t%s layout [draw|drawbin]\n", argv[0]);
    exit(1);
}

gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    double x, y, w, h;

    if (binwin)
        cr = gdk_cairo_create(gtk_layout_get_bin_window((GtkLayout *) widget));

    cairo_clip_extents(cr, &x, &y, &w, &h);
    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
    cairo_rectangle(cr, x, y, w, h);
    cairo_fill(cr);
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_set_source_rgba(cr, 0, 0, 0, 0);
    cairo_rectangle(cr, x, y, w, h);
    cairo_fill(cr);

    if (binwin)
        cairo_destroy(cr);

    return FALSE;
}

int main(int argc, char *argv[])
{
    GtkWidget *w;
    GtkWidget *q;

    parseargs(argc, argv);
    gtk_init(NULL, NULL);

    w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(w, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    if (!windowonly) {
        if (drawingarea) {
            q = gtk_drawing_area_new();
            if (viewport) {
                GtkWidget *sw;

                sw = gtk_scrolled_window_new(NULL, NULL);
                gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), q);
                q = sw;
            }
        } else {
            q = gtk_layout_new(NULL, NULL);
            if (drawsig)
                g_signal_connect(q, "draw", G_CALLBACK(draw), NULL);
        }
        gtk_container_add(GTK_CONTAINER(w), q);
    }

    gtk_widget_show_all(w);

    gtk_main();
    return 0;
}

2 个答案:

答案 0 :(得分:2)

使用

中的一个或两个向您的窗口添加GtkCssProvider
GtkLayout {
  background-color: transparent;
}

GtkViewport {
  background-color: transparent;
}

加载它。您也可以使用gtk_widget_override_background_color()执行此操作。

答案 1 :(得分:1)

他们的关键是为窗口设置正确的RGBA视觉

w = //some GtkWidget, I used the GtkWindow to test
gtk_widget_set_app_paintable (w, TRUE); // important or you will get solid color
GdkScreen *screen = gtk_widget_get_screen (w);
GdkVisual *visual = gdk_screen_get_rgba_visual (screen);
gtk_widget_set_visual(w, visual);
gtk_widget_show_all(w);

注意:也可以

g_signal_connect(G_OBJECT(w), "screen-changed", G_CALLBACK(screen_changed_contaniing_above_code), NULL);
某些多屏幕设置可能会发生奇怪的事情(无法用我的本地设置重现),但是好吧......只是这样做,你不知道在屏幕之间拖动窗口时Xinerama或其他X扩展会如何反应。

See this SO question which has a complete compileable example.


另外,我建议只使用CAIRO_OPERATOR_SOURCE绘制一次。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4
#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4
#include <gtk/gtk.h>


gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    double x, y, w, h;
    cairo_clip_extents(cr, &x, &y, &w, &h);
    cairo_set_source_rgba (cr, 1., 0., 0., 0.25); //translucent red
    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
    cairo_rectangle(cr, x, y, w, h);
    cairo_fill(cr);

    return FALSE;
}


void fix_visual(GtkWidget *w)
{
    GdkScreen *screen = gtk_widget_get_screen (w);
    GdkVisual *visual = gdk_screen_get_rgba_visual (screen);
    gtk_widget_set_visual(w, visual);
    //FIXME cleanup maybe
}


void screen_changed (GtkWidget *widget, GdkScreen *screen, gpointer user_data)
{
    fix_visual (widget);
}

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



    GtkWidget *w;
    GtkWidget *q;

    gtk_init(&argc, &argv);

    w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(w, "destroy", G_CALLBACK(gtk_main_quit), NULL);


    q = gtk_layout_new(NULL, NULL);

    g_signal_connect(w, "screen-changed", G_CALLBACK(screen_changed), NULL);
    g_signal_connect(q, "draw", G_CALLBACK(draw), NULL);

    gtk_container_add(GTK_CONTAINER(w), q);

    gtk_widget_set_app_paintable (w, TRUE);

    fix_visual (w);

    gtk_widget_show_all(w);


    gtk_main();
    return 0;
}

red with 0.5 translucent