具有选择背景颜色的 GTK+3 渲染矩形

GTK+3 render rectangle with selection background color

如何在 GTK+3 中渲染带有选区背景色的矩形。我找不到任何 API 来做到这一点:

static gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
    {
    auto state=reinterpret_cast<State*>(data);
    auto width = gtk_widget_get_allocated_width (widget);
    auto height = gtk_widget_get_allocated_height (widget);

    auto context = gtk_widget_get_style_context (widget);
    gtk_render_background(context,cr,0,0,width,height);

    cairo_rectangle(cr,0,height*(1.0 - state->max),width,height*(state->max - state->min));
    cairo_set_source_rgb(cr, 0.05,0.6,0.15); //What color should be used here?
    cairo_fill (cr);

    cairo_set_source_rgb(cr,0.01,0.3,0.07); //And here
    auto mid=height*(1.0 - 0.5*(state->min + state->max));
    cairo_move_to(cr,0, mid);
    cairo_line_to(cr,width,mid);
    cairo_stroke(cr);

    return FALSE;
    }

使用 gtk_render_frame() and gtk_render_background(),并设置您从 GtkWidget 实例获得的 GtkStyleContext 具有您要复制的 CSS 状态。

要紧扣主题,就不能画自己; CSS 没有 "colors":每个 CSS 状态可以有多个图层,包括图像、渐变和复杂的混合模式。

好吧,这是我的技巧:

ColorRGBA get_ambient_color(GtkWidget* widget)
    {
    auto surface=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,4,4);
    auto cr=cairo_create(surface);
    while(widge!=NULL)
        {
        auto context=gtk_widget_get_style_context(widget));
        gtk_render_background(context,cr,0,0,1,1);
        cairo_surface_flush(surface);
        auto content=cairo_image_surface_get_data(surface);
        if(content[3]==255)
            {
            auto ret=ColorRGBA{content[2]/255.0f,content[1]/255.0f,content[0]/255.0f,content[3]/255.0f};
            cairo_destroy(cr);
            cairo_surface_destroy(surface);
            return ret;
            }
    //  Surface is not opaque yet. Continue to parent container.
        widget_handle=gtk_widget_get_parent(GTK_WIDGET(widget_handle));
        }
    cairo_destroy(cr);
    cairo_surface_destroy(surface);
    return ColorRGBA{1.0f,1.0f,1.0f,1.0f};
    }

看来我没能说服人们,为什么你需要环境色,所以这里有两个用例:

  1. 确定我们是否使用 dark/light 主题。对于某些应用程序,这就足够了。仅当主题支持 dark/light 模式时查询状态才有效。这证明了实际结果。

  2. 用作模拟全局照明的输入颜色。小部件的阴影应该受环境影响,因此得名。另一个好名字是 get_average_background。 Themers:请不要使用高对比度的渐变。

案例 1:情节

现在你说光标和函数图的颜色应该是主题化的。这根本不可能:此绘图小部件的用户可以根据需要添加任意数量的曲线和光标,区分它们的最简单方法是使用不同的颜色。

曲线和光标亮度如何?如果背景很暗,那么曲线应该很亮,反之亦然。应该选择什么背景?理想情况下,一些东西可以关闭父小部件的背景,但如果主题是常规的,则白色代表浅色,黑色代表深色。你注意到第二张图中的曲线更暗了吗?

案例 2:看起来像金属拨动开关按钮的复选框

使用以下技术,我创建了一个开关,看起来就像是通过 Cycles 路径跟踪器渲染的一样。 这是在Gtk+2中实现的,但是算法是一样的

两张输入图片

密码

GtkAllocation alloc;
gtk_widget_get_allocation(widget,&alloc);

auto width=alloc.width;
auto context=CairoContext( gdk_cairo_create(gtk_widget_get_window(widget)) );

auto w_in=cairo_image_surface_get_width(light);
auto h_in=cairo_image_surface_get_height(light);

// Render direct lighting
auto surf_temp=CairoSurface( cairo_image_surface_create(CAIRO_FORMAT_ARGB32,w_in,h_in) );
auto context_temp=CairoContext( cairo_create(surf_temp) );
cairo_set_source_surface(context_temp,light,0,0);
cairo_set_operator(context_temp,CAIRO_OPERATOR_OVER);
cairo_paint(context_temp);

//Render ambient reflections
auto surf_temp_2=CairoSurface( cairo_image_surface_create(CAIRO_FORMAT_ARGB32,w_in,h_in) );
auto context_temp_2=CairoContext( cairo_create(surf_temp_2) );
cairo_set_source_surface(context_temp_2,background,0,0);
cairo_set_operator(context_temp_2,CAIRO_OPERATOR_OVER);
cairo_paint(context_temp_2);

cairo_set_operator(context_temp_2,CAIRO_OPERATOR_MULTIPLY);
//Multiply reflections with the background color
cairo_set_source_rgb(context_temp_2, color_bg.r, color_bg.g, color_bg.b);
cairo_rectangle(context_temp_2, 0, 0, w_in, h_in);
cairo_mask_surface(context_temp_2,surf_temp,0,0);

//Add the results
cairo_set_source_surface(context_temp,surf_temp_2,0,0);
cairo_set_operator(context_temp,CAIRO_OPERATOR_ADD);
cairo_mask_surface(context_temp,surf_temp,0,0);

//Scale and move things into place
auto s=static_cast<double>(width)/static_cast<double>(w_in);
cairo_translate(context,alloc.x,alloc.y);
cairo_scale(context,s,s);
cairo_set_source_surface(context,surf_temp,0,0);
cairo_set_operator(context,CAIRO_OPERATOR_OVER);
cairo_paint(context);

想法

第一个示例归结为 light/dark 当前缺少的查询。也许不需要查询颜色就可以工作,但是在渲染背景时必须有一个 API 来控制形状和混合模式。例如,为了渲染环境反射,我使用 multiply 而不是 over。此外,gtk_render_background 似乎是空操作,因为 GtkDrawingArea 的不透明度为零(这就是我需要循环的原因)。要发挥作用,它必须使用屏幕上显示的背景,而不是当前小部件的背景。