Source

installer / src / prettybox.vala

Full commit
using Gtk;
using Cairo;
using Gdk;

namespace Installer {

public class PrettyBox : Gtk.VBox {

   public const int SHADOW_R = 5; // Shadow radius
   public const double SHADOW_A = 0.42; // Shadow alpha


   /**
    * Construct a new PrettyBox. Essentially a shmexy VBox
    */
   public PrettyBox () {
	set_border_width (12);

	if (Config["Interface"]["Bling-Default"].to_bool ()) {
		set_app_paintable (true);
		expose_event.connect (expose);
	} // Otherwise we're just a regular ol' VBox
   }

   /**
    * Draws a rounded rectangle.
    */

   private void roundedrec(Cairo.Context cr, int x, int y, int w, int h, double r) {
	cr.move_to(x+r,y);                      // Move to A
	cr.line_to(x+w-r,y) ;                   // Straight line to B
	cr.curve_to(x+w,y,x+w,y,x+w,y+r);       // Curve to C, Control points are both at Q
	cr.line_to(x+w,y+h-r);                  // Move to D
	cr.curve_to(x+w,y+h,x+w,y+h,x+w-r,y+h); // Curve to E
	cr.line_to(x+r,y+h);                    // Line to F
	cr.curve_to(x,y+h,x,y+h,x,y+h-r);       // Curve to G
	cr.line_to(x,y+r) ;                     // Line to H
	cr.curve_to(x,y,x,y,x+r,y)  ;           // Curve to A
   }

   /**
    * Drawing requested, so go do it :)
    */
   public bool expose (Gtk.Widget widget, Gdk.EventExpose expose) {

	// Create our context
	var context = Gdk.cairo_create( window );
	var rect = expose.area;

	var surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, widget.allocation.width + 12, widget.allocation.height + 12);

	var ctx = new Cairo.Context(surface);
	ctx.set_operator (Cairo.Operator.SATURATE);

	rect.x = 10;
	rect.y = 10;
	rect.height -= 8;
	rect.width -= 8;
	roundedshadow(ctx, rect.x, rect.y, rect.width, rect.height, 6);

	this.do_draw (ctx);
	// Draw to where it matters
	context.set_source_surface (surface, allocation.x, allocation.y);
	context.paint ();

	context = null;
	return false;
   }

   /**
    * Draw to the ImageSurface in ctx
    */
   private void do_draw (Cairo.Context ctx) {
	var rect = this.allocation;

	ctx.set_operator (Cairo.Operator.OVER);
	ctx.set_line_width(0.4);
	Color color;
	Gdk.Color.parse("black", out color);

	rect.x = 10;
	rect.y = 10;
	rect.height -= 8;
	rect.width -= 8;

	ctx.set_source_rgba(color.red, color.green, color.blue, 0.52);
	ctx.stroke_preserve();
	var offset = 0; // Changing this adds a fake glass effect
	roundedrec (ctx, rect.x+offset, rect.y+offset, rect.width-offset, rect.height-offset, 6);
	ctx.set_source_rgba(color.red, color.green, color.blue, 0.29);
	ctx.fill ();
   }

   /**
    * Draw a shadowed box
    */
   private void  roundedshadow(Cairo.Context ctx, int x, int y, int w, int h, double r) {
	// top left
	var pat = new Cairo.Pattern.radial (x+r, y+r, r,
		                    x+r, y+r, r+SHADOW_R);
	pat.add_color_stop_rgba (0, 0, 0, 0, SHADOW_A);
	pat.add_color_stop_rgba (1, 0, 0, 0, 0);

	ctx.set_source (pat);
	ctx.rectangle (x-SHADOW_R, y-SHADOW_R, r+SHADOW_R, r+SHADOW_R);
	ctx.fill ();

	// top
	pat = new Cairo.Pattern.linear (0.0, y-SHADOW_R, 0.0, y);
	pat.add_color_stop_rgba (0, 0, 0, 0, 0);
	pat.add_color_stop_rgba (1, 0, 0, 0, SHADOW_A);

	ctx.set_source (pat);
	ctx.rectangle (x+r, y-SHADOW_R, w-r*2, SHADOW_R);
	ctx.fill ();

	// top right
	pat = new Cairo.Pattern.radial (w+x-r, y+r, r,
		                    w+x-r, y+r, r+SHADOW_R);
	pat.add_color_stop_rgba (0, 0, 0, 0, SHADOW_A);
	pat.add_color_stop_rgba (1, 0, 0, 0, 0);

	ctx.set_source (pat);
	ctx.rectangle (w+x-r, y-SHADOW_R, r+SHADOW_R, r+SHADOW_R);
	ctx.fill ();

	// right
	pat = new Cairo.Pattern.linear (w+x, 0.0, w+x+SHADOW_R, 0.0);
	pat.add_color_stop_rgba (0, 0, 0, 0, SHADOW_A);
	pat.add_color_stop_rgba (1, 0, 0, 0, 0);

	ctx.set_source (pat);
	ctx.rectangle (w+x, y+r, SHADOW_R, h-r*2);
	ctx.fill ();

	// bottom right
	pat = new Cairo.Pattern.radial (w+x-r, h+y-r, r,
		                    w+x-r, h+y-r, r+SHADOW_R);
	pat.add_color_stop_rgba (0, 0, 0, 0, SHADOW_A);
	pat.add_color_stop_rgba (1, 0, 0, 0, 0);

	ctx.set_source (pat);
	ctx.rectangle (w+x-r, h+y-r, r+SHADOW_R, r+SHADOW_R);
	ctx.fill ();

	// bottom
	pat = new Cairo.Pattern.linear (0.0, h+y, 0.0, h+y+SHADOW_R);
	pat.add_color_stop_rgba (0, 0, 0, 0, SHADOW_A);
	pat.add_color_stop_rgba (1, 0, 0, 0, 0);

	ctx.set_source (pat);
	ctx.rectangle (x+r, h+y, w-r*2, SHADOW_R);
	ctx.fill ();

	// bottom left
	pat = new Cairo.Pattern.radial (x+r, h+y-r, r,
		                    x+r, h+y-r, r+SHADOW_R);
	pat.add_color_stop_rgba (0, 0, 0, 0, SHADOW_A);
	pat.add_color_stop_rgba (1, 0, 0, 0, 0);

	ctx.set_source (pat);
	ctx.rectangle (x-SHADOW_R, h+y-r, r+SHADOW_R, r+SHADOW_R);
	ctx.fill ();

	// left
	pat = new Cairo.Pattern.linear (x-SHADOW_R, 0.0, x, 0.0);
	pat.add_color_stop_rgba (0, 0, 0, 0, 0);
	pat.add_color_stop_rgba (1, 0, 0, 0, SHADOW_A);

	ctx.set_source (pat);
	ctx.rectangle (x-SHADOW_R, y+r, r+SHADOW_R, h-r*2);
	ctx.fill ();
	
	roundedrec(ctx, x, y, w, h, r);
	ctx.set_operator (Cairo.Operator.CLEAR);
	ctx.fill();
   }

} // End PrettyBox class definition

} // End namespace