GTK+ 3.0, enabling incrementalism

This is a reply to some comments on Kris blog post about our vision of GTK+ 3.0.

First off, as I said in my reply there, I think it’s important that we do not make GTK+ 3.0 into a holy grail of all the features we would like to see in GTK+. There will always be features lined up that would make sense to push in, some which will change fundamental things in the core.

The way I see it (which Havoc seem to as well) is that a change of the Widget core to a scene graph is a project that will take place outside of the GTK+ tree and at some point get endorsed and included. This is a process that is likely to take several years and I do not see any reason to put GTK+ on hold, waiting for it.

There are still things that can be done on the GTK+ 2.x tree without breaking ABI. However, it is getting harder and harder (in some cases already impossible) to do it in a compatible way. A tremendous effort is put on figuring out all the places that can possibly break external ABI due to an internal change.

Mind though that this is not only the case when it comes to new features but also day-to-day maintenance is suffering from this with several bugs that can’t even be fixed today.

GTK+ currently exposes a huge amount of internal implementation details which blocks cleaning up of internal code, something that is much needed. This is not only for code beautification, in fact, the reason is that it’s a necessity in order to maintain the toolkit.

Why do we propose to break ABI now and not after features X, Y and Z? Because we want to do the break at a point where users will not have to make any logical changes to their code (as long as they are not using deprecated widgets or functions).

For applications being fairly up to date it will be a matter of recompiling with the right set of flags and then let the compiler do its work and fix the places where it breaks. Our plan for this is the introduction of the GSEAL macro that will let you compile your application with:

  CFLAGS="-DGSEAL_ENABLE -DDISABLE_DEPRECATED" ./configure

This will make the compiler catch all uses of direct access to the object fields so that you can go through them one by one and replace them with a call to an accessor function instead. This can even be automated in many cases and Johan tried it with some nifty regexps yesterday which seemed to work fine.

The changes will in a majority of the cases be:

  Bar *bar = foo_get_bar (foo);

instead of:

  Bar *bar = foo->bar;

After we have done this we will have an API consisting only of functions which means that any compatibility glue code we need to write can be done without the risk of variables being accessed directly and circumvent the compatibility layer. This is in the fundamentals of OOP practices and I believe that it’s a price well worth paying.

More over, since 3.0 is not going to be a feature release there is no need for applications to jump on it right away, they can use that period to do any transitions needed and be in position to reap the benefits with the ABI compatible 3.2 feature release.

Will we have to break again in the future? Of course! That’s what 4.0 is for and we propose that we already at the release of 3.0 decides when 4.0 is planned to be released so people know when deprecated code will be removed.

Michael wrote in Kris blog that we shouldn’t discard incrementalism, and this is the exact same feeling we have. We need to do things incrementally, but that doesn’t mean things can or have to always stay 100% compatible, only that the changes are small to ease the effort of following them.

In summary our proposed solution is an enabler for incrementalism.

Subclassing in Ruby/GTK+

Davyd is spot on when bringing up the use of subclassing in GTK+. This is a step that comes natural for most people used to OO programming but (probably due to having to write a lot of boiler plate code) often is neglected by GTK+ application developers.

While many feel it’s daunting to subclass GTK+ widgets in C, it can be really nice when writing applications in other languages. Just like Davyd showed, Python is such a language, so is Ruby which I will show a similar brief example of here for those that are trying to get into Ruby/GTK+.

require 'gtk2'

# Defining OpenFileDialog
class OpenFileDialog < Gtk::FileChooserDialog
  @@CWD = nil

  def initialize(parent_window)
    super("Open file", parent_window,
          Gtk::FileChooser::ACTION_OPEN, nil,
          [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
          [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_OK])

    self.default_response = Gtk::Dialog::RESPONSE_OK

    self.current_folder = @@CWD unless @@CWD.nil?

    signal_connect :response do |dialog, response|
      @@CWD = self.current_folder if response == Gtk::Dialog::RESPONSE_OK
    end
  end
end

# Using OpenFileDialog
2.times do
  d = OpenFileDialog.new(nil)

  d.run do |response|
    puts "filename = #{d.filename}" if response == Gtk::Dialog::RESPONSE_OK
  end

  d.destroy
end

The second half simply to try it out. For a nicer paste, see http://pastie.caboo.se/148847.

But it’s so hard in C!
Writing all the boilerplate by hand is a lot of work and error prone. However, here are two tips for making life easier.

The first way is to implement a dummy object that you later copy and query/replace. A nice tool for using this method is Regexxer) that lets you easily change the dummy name into your object name. I suggest you hook up your dummy object in the Makefile in order to make sure it always builds.

The other solution is to use a generator script such as Spuug to generate all the boilerplate for you. It makes it very easy to create new subclasses when you need them.

Update: As Johannes pointed out in a comment, Anjuta also comes with a generator for subclassing.

Drawing with Cairo in Ruby-Gtk2

With my rekindled love for Ruby I started hang around in #ruby-lang again and got the question how you use Cairo to draw inside a Ruby-Gtk2 program. I cooked up a small example and figured that sharing it here might be useful for others as well.

Otherwise you can just enjoy the cleanness of Ruby ;)

#!/usr/bin/env ruby

require 'gtk2'

window = Gtk::Window.new

window.signal_connect("delete-event") do
  Gtk.main_quit
  true
end

area = Gtk::DrawingArea.new
area.set_size_request(100,100)

area.signal_connect("expose-event") do
  cairo = area.window.create_cairo_context

  cairo.rectangle(10, 10, 50, 50)
  cairo.fill
  true
end

window.add(area)
window.show_all

Gtk.main

« Previous PageNext Page »

Close
E-mail It