Genie Cairo Example
uses
Gtk
Cairo
class CairoSample : Gtk.Window
const private SIZE : int = 30
construct ()
self.title = "Cairo Genie Demo"
self.destroy.connect (Gtk.main_quit)
set_default_size (450, 550)
create_widgets ()
def private create_widgets ()
var drawing_area = new DrawingArea ()
drawing_area.draw.connect (on_draw)
add (drawing_area)
def private on_draw (da : Widget, ctx : Context) : bool
ctx.set_source_rgb (0, 0, 0)
ctx.set_line_width (SIZE / 4)
ctx.set_tolerance (0.1)
ctx.set_line_join (LineJoin.ROUND)
dbl_arr : array of double = {SIZE / 4.0, SIZE / 4.0}
ctx.set_dash ( dbl_arr, 0)
stroke_shapes (ctx, 0, 0)
ctx.set_dash (null, 0)
stroke_shapes (ctx, 0, 3 * SIZE)
ctx.set_line_join (LineJoin.BEVEL)
stroke_shapes (ctx, 0, 6 * SIZE)
ctx.set_line_join (LineJoin.MITER)
stroke_shapes(ctx, 0, 9 * SIZE)
fill_shapes (ctx, 0, 12 * SIZE)
ctx.set_line_join (LineJoin.BEVEL)
fill_shapes (ctx, 0, 15 * SIZE)
ctx.set_source_rgb (1, 0, 0)
stroke_shapes (ctx, 0, 15 * SIZE)
return true
def private stroke_shapes (ctx : Context, x : int, y : int)
self.draw_shapes (ctx, x, y, ctx.stroke)
def private fill_shapes (ctx : Context, x : int, y : int)
self.draw_shapes (ctx, x, y, ctx.fill)
delegate private DrawMethod ()
def private draw_shapes (ctx : Context, x : int, y : int, draw_method : DrawMethod)
ctx.save ()
ctx.new_path ()
ctx.translate (x + SIZE, y + SIZE)
bowtie (ctx)
draw_method ()
ctx.new_path ()
ctx.translate (3 * SIZE, 0)
square (ctx)
draw_method ()
ctx.new_path ()
ctx.translate (3 * SIZE, 0)
triangle (ctx)
draw_method ()
ctx.new_path ()
ctx.translate (3 * SIZE, 0)
inf (ctx)
draw_method ()
ctx.restore()
def private triangle (ctx : Context)
ctx.move_to (SIZE, 0)
ctx.rel_line_to (SIZE, 2 * SIZE)
ctx.rel_line_to (-2 * SIZE, 0)
ctx.close_path ()
def private square (ctx : Context)
ctx.move_to (0, 0)
ctx.rel_line_to (2 * SIZE, 0)
ctx.rel_line_to (0, 2 * SIZE)
ctx.rel_line_to (-2 * SIZE, 0)
ctx.close_path ()
def private bowtie (ctx : Context)
ctx.move_to (0, 0)
ctx.rel_line_to (2 * SIZE, 2 * SIZE)
ctx.rel_line_to (-2 * SIZE, 0)
ctx.rel_line_to (2 * SIZE, -2 * SIZE)
ctx.close_path ()
def private inf (ctx : Context)
ctx.move_to (0, SIZE)
ctx.rel_curve_to (0, SIZE, SIZE, SIZE, 2 * SIZE, 0)
ctx.rel_curve_to (SIZE, -SIZE, 2 * SIZE, -SIZE, 2 * SIZE, 0)
ctx.rel_curve_to (0, SIZE, -SIZE, SIZE, -2 * SIZE, 0)
ctx.rel_curve_to (-SIZE, -SIZE, -2 * SIZE, -SIZE, -2 * SIZE, 0)
ctx.close_path ()
init
Gtk.init (ref args)
var cairo_sample = new CairoSample ()
cairo_sample.show_all ()
Gtk.main ()
Compile and Run
$ valac --pkg gtk+-3.0 cairosample.gs $ ./cairosample
Shaped Window Example
uses
Gtk
Cairo
/**
* This example creates a clock with the following features:
* Shaped window -- Window is unbordered and transparent outside the clock
* Events are only registered for the window on the hour dots or on the center
* dot. When the mouse is "in" the window (on one of the dots) it will turn
* green.
* This helps you understand where the events are actually being registered
* Clicking allows you to drag the clock.
* There is currently no code in place to close the window, you must kill the
* process manually. A Composited environment is required. The python code I
* copied this from includes checks for this. In my laziness I left them out.
*/
class CairoShaped : Gtk.Window
// Are we inside the window?
inside : bool = false
/**
* Just creating the window, setting things up
*/
construct ()
self.title = "Cairo Vala Demo"
set_default_size (200, 200)
// 'skip_taskbar_hint' determines whether the window gets an icon in
// the taskbar / dock
this.skip_taskbar_hint = true
// Turn off the border decoration
this.decorated = false
this.app_paintable = true
// Need to get the RGBA colormap or transparency doesn't work.
set_colormap (this.screen.get_rgba_colormap ())
// We need to register which events we are interested in
add_events (Gdk.EventMask.BUTTON_PRESS_MASK)
add_events (Gdk.EventMask.ENTER_NOTIFY_MASK)
add_events (Gdk.EventMask.LEAVE_NOTIFY_MASK)
// Connecting some events, 'queue_draw()' redraws the window.
// 'begin_move_drag()' sets up the window drag
self.enter_notify_event.connect( enter_event )
self.leave_notify_event.connect ( leave_event )
self.button_press_event.connect ( handle_btn_press )
// The expose event is what is called when we need to draw the window
this.expose_event.connect (on_expose)
this.destroy.connect (Gtk.main_quit)
def handle_btn_press( e : Gdk.EventButton ) : bool
begin_move_drag ((int) e.button, (int) e.x_root, (int) e.y_root, e.time)
return true
def leave_event() : bool
self.inside = false
self.queue_draw ()
return true
def enter_event() : bool
self.inside = true
self.queue_draw ()
return true
/**
* Actual drawing takes place within this method
*/
def private on_expose (da : Widget, event : Gdk.EventExpose) : bool
// Get a cairo context for our window
var ctx = Gdk.cairo_create (da.window)
// This makes the current color transparent (a = 0.0)
ctx.set_source_rgba (1.0, 1.0, 1.0, 0.0)
// Paint the entire window transparent to start with.
ctx.set_operator (Cairo.Operator.SOURCE)
ctx.paint ()
// If we wanted to allow scaling we could do some calculation here
radius : float = 100
// This creates a radial gradient. c() is just a helper method to
// convert from 0 - 255 scale to 0.0 - 1.0 scale.
var p = new Cairo.Pattern.radial (100, 100, 0, 100, 100, 100)
if inside
p.add_color_stop_rgba (0.0, c (10), c (190), c (10), 1.0)
p.add_color_stop_rgba (0.8, c (10), c (190), c (10), 0.7)
p.add_color_stop_rgba (1.0, c (10), c (190), c (10), 0.5)
else
p.add_color_stop_rgba (0.0, c (10), c (10), c (190), 1.0)
p.add_color_stop_rgba (0.8, c (10), c (10), c (190), 0.7)
p.add_color_stop_rgba (1.0, c (10), c (10), c (190), 0.5)
// Set the gradient as our source and paint a circle.
ctx.set_source (p)
ctx.arc (100, 100, radius, 0, 2.0 * 3.14)
ctx.fill ()
ctx.stroke ()
// This chooses the color for the hour dots
if inside
ctx.set_source_rgba (0.0, 0.2, 0.6, 0.8)
else
ctx.set_source_rgba (c (226), c (119), c (214), 0.8)
// Draw the 12 hour dots.
for var i = 0 to 12
ctx.arc (100 + 90.0 * Math.cos (2.0 * 3.14 * (i / 12.0)),
100 + 90.0 * Math.sin (2.0 * 3.14 * (i / 12.0)),
5, 0, 2.0 * 3.14)
ctx.fill ()
ctx.stroke ()
// This is the math to draw the hands.
// Nothing overly useful in this section
ctx.move_to (100, 100)
ctx.set_source_rgba (0, 0, 0, 0.8)
var t = Time.local (time_t ())
hour : int = t.hour
minutes : int = t.minute
seconds :int = t.second
per_hour : double = (2 * 3.14) / 12
dh : double = (hour * per_hour) + ((per_hour / 60) * minutes)
dh += 2 * 3.14 / 4
ctx.set_line_width (0.05 * radius)
ctx.rel_line_to (-0.5 * radius * Math.cos (dh), -0.5 * radius * Math.sin (dh))
ctx.move_to (100, 100)
per_minute : double = (2 * 3.14) / 60
dm : double = minutes * per_minute
dm += 2 * 3.14 / 4
ctx.rel_line_to (-0.9 * radius * Math.cos (dm), -0.9 * radius * Math.sin (dm))
ctx.move_to (100, 100)
per_second :double = (2 * 3.14) / 60
ds :double = seconds * per_second
ds += 2 * 3.14 / 4
ctx.rel_line_to (-0.9 * radius * Math.cos (ds), -0.9 * radius * Math.sin (ds))
ctx.stroke ()
// Drawing the center dot
ctx.set_source_rgba (c (124), c (32), c (113), 0.7)
ctx.arc (100, 100, 0.1 * radius, 0, 2.0 * 3.14)
ctx.fill ()
ctx.stroke ()
// This is possibly the most important bit.
// Here is where we create the mask to shape the window
// And decide what areas will receive events and which areas
// Will let events pass through to the windows below.
// First create a pixmap the size of the window
var px = new Gdk.Pixmap (null, 200, 200, 1)
// Get a context for it
var pmcr = Gdk.cairo_create (px)
// Initially we want to blank out everything in transparent as we
// Did initially on the ctx context
pmcr.set_source_rgba (1.0, 1.0, 1.0, 0.0)
pmcr.set_operator (Cairo.Operator.SOURCE)
pmcr.paint ()
// Now the areas that should receive events need to be made opaque
pmcr.set_source_rgba (0, 0, 0, 1)
// Here we copy the motions to draw the middle dots and the hour dots.
// This is mostly to demonstrate that you can make this any shape you
// want.
pmcr.arc (100, 100, 10, 0, 2.0 * 3.14)
pmcr.fill ()
pmcr.stroke ()
for var i = 0 to 12 // ATTENTION -> divide by zero ?
pmcr.arc (100 + 90.0 * Math.cos (2.0 * 3.14 * (i / 12.0)),
100 + 90.0 * Math.sin (2.0 * 3.14 * (i / 12.0)),
5, 0, 2.0 * 3.14)
pmcr.fill ()
pmcr.stroke ()
// This sets the mask. Note that we have to cast to a Gdk.Bitmap*,
// it won't compile without that bit.
input_shape_combine_mask ((Gdk.Bitmap*) px, 0, 0)
return true
def private c (val : int) : double
return val / 255.0
def updatedraw() : bool
self.queue_draw()
return true
init
Gtk.init (ref args)
var cairo_sample = new CairoShaped ()
cairo_sample.show_all ()
// Just a timeout to update once a second.
Timeout.add_seconds (1, cairo_sample.updatedraw )
Gtk.main ()
Compile and Run
$ valac --pkg gtk+-2.0 --pkg cairo --pkg gdk-2.0 cairo-shaped.gs $ ./cairo-shaped