How to achieve smooth rounded corners in gtk3

I’m trying to achieve smooth rounding of corners for a child widget. In order to apply this rounding to nested widgets later on, I use region clipping, but the corners look jagged. Is there a way to do this with antialiasing in gtk3? I use this code:


#include <gtk/gtk.h>
#include <gtk/gtkx.h>
#include <cairo.h>

static void set_rounded_corners(GtkWidget *widget, double radius)
{
    if (GdkWindow *gdk_window = gtk_widget_get_window(widget)) {
        int width = gtk_widget_get_allocated_width(widget);
        int height = gtk_widget_get_allocated_height(widget);

        cairo_surface_t *sfc = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
        cairo_t *cr = cairo_create(sfc);

        cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);
        cairo_set_source_rgba(cr, 1, 1, 1, 1);
        cairo_move_to(cr, radius, 0);
        cairo_arc(cr, width - radius, radius, radius, -G_PI_2, 0);
        cairo_arc(cr, width - radius, height - radius, radius, 0, G_PI_2);
        cairo_arc(cr, radius, height - radius, radius, G_PI_2, G_PI);
        cairo_arc(cr, radius, radius, radius, G_PI, -G_PI_2);
        cairo_close_path(cr);
        cairo_fill(cr);

        cairo_surface_t *sfc_tgt = cairo_get_target(cr);
        cairo_surface_flush(sfc_tgt);

        cairo_region_t *mask = gdk_cairo_region_create_from_surface(sfc_tgt);
        gdk_window_shape_combine_region(gdk_window, mask, 0, 0);

        cairo_region_destroy(mask);
        cairo_destroy(cr);
        cairo_surface_destroy(sfc);
    }
}

static void on_size_allocate(GtkWidget *widget, GdkRectangle*, gpointer)
{
    set_rounded_corners(widget, 6);
}

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

    ... // create top_level_window window
    gtk_widget_set_app_paintable(top_level_window, TRUE);
    GdkScreen *scr = gtk_widget_get_screen(top_level_window);
    GdkVisual *vis = gdk_screen_get_rgba_visual(scr);
    gtk_widget_set_visual(top_level_window, vis);

    ... // create child_widget
    g_signal_connect(G_OBJECT(child_widget), "size-allocate", G_CALLBACK(on_size_allocate), NULL);

    gtk_main();
}

That’s not a limitation of GTK: it’s a limitation of the region code and—since you’re including gdkx.h I assume you’re using X11—it’s a limitation of the X11 shaping extension.

If you want anti-aliased rounded corners when drawing, you should set the border radius of the window using CSS you load in your own application, via gtk_style_context_add_provider_for_screen(); on top of that, use gdk_window_input_shape_combine_region() to set the input region.

I just use GtkSocket as a child widget, and it doesn’t have CSS rounding applied to it.

There was an idea to change the background of the main window to mask the torn edges with the window color, but for some reason I can’t apply the color to the window background via CSS, it’s not clear.

Embedded X windows cannot be transparent, so CSS cannot change their background.

You are doing something that is not entirely trivial, and you’re using a very old API to do it.

Instead of CSS you could override the GtkWidget::draw virtual function, but again: you’ll end up hitting the fact that you’re trying to mask and draw a native windowing system surface that has no concept of anti-aliasing. In short: what you want to achieve is not really possible.

The background option turned out to be working, I use it for the main window to mask the unevenness of the rounding when using gdk_window_shape_combine_region.

GdkRGBA c;
gdk_rgba_parse(&c, “0000ff”);
gtk_widget_override_background_color(top_level_window, GTK_STATE_FLAG_NORMAL, &c);