yuanjq91 发表于 2013-8-25 22:35:07

gtk窗口边界阴影效果实现

gtk默认窗口管理器是带阴影效果的,但是当我们自定义界面去除窗口管理器的修饰后阴影效果就消失了。学习了下deepin-ui关于窗口阴影效果处理,改用gtk3完美实现阴影效果,发个demo分享下,具体步骤就不细说了,看代码你懂的。下图是效果:

// 编译命令 gcc *.c `pkg-config --cflags --libs gtk+-3.0`
#include <gtk/gtk.h>

gint frame_radius = 3; // 2
gint shadow_radius = 10;// 6
gint shadow_padding = 0;

void draw_round_rectangle(cairo_t *cr, gint x, gint y, gint width, gint height, gint r);
void draw_radial_round(cairo_t *cr, gint x, gint y, gint r);
void draw_hlinear(cairo_t *cr, gint x, gint y, gint w, gint h, gboolean left_to_right);
void draw_vlinear(cairo_t *cr, gint x, gint y, gint w, gint h, gboolean top_to_bottom);

void draw_window_shadow(cairo_t *cr, gint x, gint y, gint w, gint h, gint r, gint p)
{
    cairo_save(cr);
    cairo_rectangle(cr, x, y, r - 1, r - 1);
    cairo_rectangle(cr, x + r - 1, y, 1, r - 2);
    cairo_rectangle(cr, x, y + r - 1, r - 2, 1);
   
    cairo_rectangle(cr, x + w - r + 1, y, r - 1, r - 1);
    cairo_rectangle(cr, x + w - r, y, 1, r - 2);
    cairo_rectangle(cr, x + w - r + 2, y + r - 1, r - 2, 1);
   
    cairo_rectangle(cr, x, y + h - r + 1, r - 1, r - 1);
    cairo_rectangle(cr, x + r - 1, y + h - r + 2, 1, r - 2);
    cairo_rectangle(cr, x, y + h - r, r - 2, 1);
   
    cairo_rectangle(cr, x + w - r + 1, y + h - r + 1, r - 1, r - 1);
    cairo_rectangle(cr, x + w - r, y + h - r + 2, 1, r - 2);
    cairo_rectangle(cr, x + w - r + 2, y + h - r, r - 2, 1);
    cairo_clip(cr);
   
    // Draw four round.
    draw_radial_round(cr, x + r, y + r, r);
    draw_radial_round(cr, x + r, y + h - r, r);
    draw_radial_round(cr, x + w - r, y + r, r);
    draw_radial_round(cr, x + w - r, y + h - r, r);
    cairo_restore(cr);
   
    cairo_save(cr);
    // Clip four side.
    cairo_rectangle(cr, x, y + r, p, h - r * 2);
    cairo_rectangle(cr, x + w - p, y + r, p, h - r * 2);
    cairo_rectangle(cr, x + r, y, w - r * 2, p);
    cairo_rectangle(cr, x + r, y + h - p, w - r * 2, p);
    cairo_clip(cr);
   
    // Draw four side.
    draw_vlinear(
      cr,
      x + r, y,
      w - r * 2, r, TRUE);
    draw_vlinear(
      cr,
      x + r, y + h - r,
      w - r * 2, r, FALSE);
    draw_hlinear(
      cr,
      x, y + r,
      r, h - r * 2, TRUE);
    draw_hlinear(
      cr,
      x + w - r, y + r,
      r, h - r * 2, FALSE);
    cairo_restore(cr);
}

void draw_radial_round(cairo_t *cr, gint x, gint y, gint r)
{
    cairo_pattern_t *radial = cairo_pattern_create_radial(x, y, r, x, y, 0);
    cairo_pattern_add_color_stop_rgba(radial, 0, 0, 0, 0, 0);
    cairo_pattern_add_color_stop_rgba(radial, 0.66, 0, 0, 0, 0.2);
    cairo_pattern_add_color_stop_rgba(radial, 1, 0, 0, 0, 0.33);
    cairo_arc(cr, x, y, r, 0, 2 * G_PI);
    cairo_set_source(cr, radial);
    cairo_fill(cr);

    cairo_pattern_destroy(radial);
}

void draw_vlinear(cairo_t *cr, gint x, gint y, gint w, gint h, gboolean top_to_buttom)
{
    gint r = 0;
    cairo_save(cr);
    // Translate y coordinate, otherwise y is too big for LinearGradient cause render bug.
    cairo_translate(cr, 0, y);
    cairo_pattern_t *linear = NULL;
    if(top_to_buttom)
      linear = cairo_pattern_create_linear(0, 0, 0, h);
    else
      linear = cairo_pattern_create_linear(0, h, 0, 0);

    cairo_pattern_add_color_stop_rgba(linear, 0, 0, 0, 0, 0);
    cairo_pattern_add_color_stop_rgba(linear, 0.66, 0, 0, 0, 0.2);
    cairo_pattern_add_color_stop_rgba(linear, 1, 0, 0, 0, 0.33);
      
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_set_source(cr, linear);
    draw_round_rectangle(cr, x, 0, w, h, r);
    cairo_fill(cr);
    cairo_restore(cr);
    cairo_pattern_destroy(linear);
}

void draw_hlinear(cairo_t *cr, gint x, gint y, gint w, gint h, gboolean left_to_right)
{
    gint r = 0;
    cairo_save(cr);
    // Translate x coordinate, otherwise x is too big for LinearGradient cause render bug.
    cairo_pattern_t *linear;
    cairo_translate(cr, x, 0);
    if(left_to_right)   
      linear = cairo_pattern_create_linear(0, 0, w, 0);
    else
      linear = cairo_pattern_create_linear(w, 0, 0, 0);

    cairo_pattern_add_color_stop_rgba(linear, 0, 0, 0, 0, 0);
    cairo_pattern_add_color_stop_rgba(linear, 0.66, 0, 0, 0, 0.2);
    cairo_pattern_add_color_stop_rgba(linear, 1, 0, 0, 0, 0.33);

    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_set_source(cr, linear);
    draw_round_rectangle(cr, 0, y, w, h, r);
    cairo_fill(cr);
    cairo_restore(cr);

    cairo_pattern_destroy(linear);
}

void draw_round_rectangle(cairo_t *cr, gint x, gint y, gint width, gint height, gint r)
{
    // Adjust coordinate when width and height is negative.
    if(width < 0)
    {
      x = x + width;
      width = -width;
    }

    if(height < 0)
    {
      y = y + height;
      height = -height;
    }
   
    // Top side.
    cairo_move_to(cr, x + r, y);
    cairo_line_to(cr, x + width - r, y);
    // Top-right corner.
    cairo_arc(cr, x + width - r, y + r, r, G_PI * 3 / 2, G_PI * 2);
    // Right side.
    cairo_line_to(cr, x + width, y + height - r);
    // Bottom-right corner.
    cairo_arc(cr, x + width - r, y + height - r, r, 0, G_PI / 2);
    // Bottom side.
    cairo_line_to(cr, x + r, y + height);
   
    // Bottom-left corner.
    cairo_arc(cr, x + r, y + height - r, r, G_PI / 2, G_PI);
   
    // Left side.
    cairo_line_to(cr, x, y + r);
    // Top-left corner.
    cairo_arc(cr, x + r, y + r, r, G_PI, G_PI * 3 / 2);

    // Close path.
    cairo_close_path(cr);
}

gboolean on_draw_shadow(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    cr = gdk_cairo_create(gtk_widget_get_window(widget));

    GtkAllocation alloc;
    gtk_widget_get_allocation(widget, &alloc);
    draw_window_shadow(cr, alloc.x, alloc.y, alloc.width, alloc.height, shadow_radius, shadow_padding);
}

gboolean on_draw(GtkWidget *widget, gpointer data)
{
    GtkAllocation alloc;
    gint x, y, width, height, r;
    gtk_widget_get_allocation(widget, &alloc);
    x = alloc.x;
    y = alloc.y;
    width = alloc.width;
    height = alloc.height;
    r = 5;

    cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));

    cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
    cairo_t *cr2 = cairo_create(surface);

    cairo_move_to(cr2, r, 0);
    cairo_line_to(cr2, width-r, 0);

    cairo_move_to(cr2, width, r);
    cairo_line_to(cr2, width, height-r);

    cairo_move_to(cr2, width-r, height);
    cairo_move_to(cr2, r, height);

    cairo_move_to(cr2, 0, height-r);
    cairo_line_to(cr2, 0, r);

    cairo_arc(cr2, r, r, r, G_PI, 3 * G_PI / 2.0 );
    cairo_arc(cr2, width-r, r, r, 3 * G_PI /2.0, 2 * G_PI);
    cairo_arc(cr2, width-r, height-r, r, 0, G_PI / 2);
    cairo_arc(cr2, r, height-r, r, G_PI / 2, G_PI);
    cairo_fill(cr2);

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

    cairo_region_t *region = gdk_cairo_region_create_from_surface(surface);
    gtk_widget_shape_combine_region(widget, region);

    cairo_destroy(cr2);
    cairo_destroy(cr);
    cairo_region_destroy(region);
    cairo_surface_destroy(surface);

    return FALSE;
}

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

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
    gtk_widget_set_app_paintable(window, TRUE);
    g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(on_draw), NULL);
    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

    shadow_padding = shadow_radius - frame_radius;
    GtkWidget *window_shadow = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
    gtk_alignment_set_padding(GTK_ALIGNMENT(window_shadow), shadow_padding, shadow_padding, shadow_padding, shadow_padding);
    g_signal_connect(G_OBJECT(window_shadow), "draw", G_CALLBACK(on_draw_shadow), NULL);

    /*GtkWidget *button = gtk_button_new_with_label("Hello Gtk");*/
    /*gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);*/
    /*gtk_container_add(GTK_CONTAINER(window_shadow), button);*/

    gtk_container_add(GTK_CONTAINER(window), window_shadow);

    GdkScreen *screen;
    GdkVisual *visual;
    screen = gdk_screen_get_default();
    visual = gdk_screen_get_rgba_visual(screen);
    gtk_widget_set_visual(window, visual);

    gtk_widget_show_all(window);
    gtk_main();
    return 0;
}


白浪 发表于 2013-8-25 22:39:01

不错- -

lifulinghan 发表于 2013-12-19 13:03:31

你的代码会挂

apphe7 发表于 2013-12-31 10:18:01

请教一下,这个必须用gtk3才能编译通过吗?我用gtk2提示“undefined reference to `gdk_cairo_region_create_from_surface”。。。

ttttt 发表于 2014-10-29 23:05:26

页: [1]
查看完整版本: gtk窗口边界阴影效果实现