Wednesday, January 16, 2008

Cairo and the GTK in Ruby. Creating Custom Widgets.

This post is more about programming than it is about OpenFracas.

When I was trying to learn how to use cairo to create custom widgets in the GTK with ruby, I ran into a real lack of documentation online. There were lists of functions and all, but lists of functions just don't compare to samples, as far as I'm concerned. So I've decided to put together a little bit of code that can be used as a how-to or as a starting point for someone else's projects

The following Ruby code is an example of a custom GTK control created using the RCairo SVG library (cairo is built into GTK 2.8+) by extending the Gtk::DrawingArea class.



#we need to include these for obvious reasons
require 'gtk2'
require 'cairo'

#This class extends Gtk::DrawingArea
class
CustomWidget < Gtk::DrawingArea
def initialize
#call Gtk::DrawingArea's constructor first
super()

#connect the expose event
#(means drawing is needed)
#to the fRedraw function
self
.signal_connect("expose_event") do

fRedraw
end

end


#called when we get an expose event
def fRedraw
#create our drawing context.#whatever we draw on this ends up on the screen #self.window is a Gdk::Window
context = self.window.create_cairo_context

#get the actual height/width of this control
height = self.allocation.height.to_f
width = self.allocation.width.to_f

#make drawing occur offscreen (double-buffering)
rect = Gdk::Rectangle.new(0, 0, self.allocation.width, self.allocation.height)
self.window.begin_paint(rect)

#set the colour and flood the region
c.set_source_rgba(0.5, 0.4, 0.6, 0.9)
c.paint

#set the colour and draw a line
c.set_source_rgba(0, 0, 0, 0.9)
c.move_to(height / 2.0, width / 2.0)
c.rel_line_to(height, width)
c.stroke

self.window.end_paint

end

end

That is a very basic example. From what I understand, it would be a good idea to try and buffer it by using an ImageSurface and creating a graphics context for it, then drawing to the ImageSurface's context. Once you're done, you can set the window context's source to the ImageSurface, and .fill the area.

for example, to create the image

@image = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, height, width) @imageContext = Cairo::Context.new(@image)

Then in the fRedraw function where context is the window's context

context.set_source(@image, 0, 0)
context.paint

You'll still need to add this control to a parent control such as a window or a layout container. You'll also need to .show it for it to be visible.

For drawing things with a fixed scale, you can use the .scale command to scale a context down to a specific size. There's a really good tutorial for using cairo for drawing here. It's in Python, but it's still very helpful.

No comments: