Making You Own Widget

A few days ago, I was in gtk+ irc channel and there was someone making the kind of question I’ve done myself like a hundred times. Thankfully the guys in the channel hasn’t got tired of me, yet. They are pretty helpful once they realize you’ve done your homework.

Once you start hacking around with Gtk+, the next thing you’ll want to do is to make your own widgets. And for that there’s little to none documentation, guides or tutorial. There’s a huge amount of knowledge in Gtk+ source itself, but most people doesn’t like to read code, or doesn’t know how to. They should, though

Here I’ll try to show, from the newbie point, how Gtk+ draw things, and what’s the guidelines to accomplish certain tasks when making widgets for Gtk+. This is, as well, a reminder for myself, since most times I need to go into IRC to ask the same things all over again.

I will split the post in three main sections which corresponds to the important parts in a widget lifetime.

Size handling

One of the thing you need when you’re making widgets is knowing the dimensions everything will have inside. Size requisition and allocation is the step where this happens.

Explanations of this process appears here in GtkWidget’s documentation. It includes most of the size requisition process and geometry management.

Gtk+ widgets are arranged in a hierarchy where container widgets pack others and handle its size and allocation. The widget containing yours will ask the natural and minimal size you want to have, and after making some internal calculations will assign your widget a size and allocation. The first part is called size requisition and the second size allocation. The virtual methods you need to implement here are:

Now, what to do on each one of these methods ?, Calculate what size you want for the widget, and return it. Let’s say you have a widget which have a text saying “Hello” inside, then, you need to ask Pango to calculate the width and height for you, add the margins and padding (if you want to take those into account) and return the size accordingly.

What’s width-for-height and height-for-width methods for ?, Widget have by definition one request-mode, its width depends on its height or the other way around. This is totally decided by you, accordingly to the type of widget you’re creating. The purpose of these methods is to retrieve these measures, the width of a widget given a specified height, and viceversa. About this part, read carefully the docs above and go asking in the IRC for more help if stuck.

One thing to keep in mind is that the allocated size to your widget, could be not the same you requested in the get_preferred methods, so the widget should work with the given allocation instead of any other measure.

Rule of thumb: Implement the four methods of size requisition almost always, and the size-allocate one if needed.

Drawing stuff

This is the core of the matter, but there’s no much to tell in here. Use cairo to draw, the API is pretty simple. Use cairo primitives to draw inside the implementation of draw virtual method. There, is given a context which is clipped from one of the GdkWindow of the widget, you should draw in that context. Other than this, here are some remarks:

  • How to draw an icon from the theme into the cairo_context_t ?. Obtain a GdkPixbuf from the icon with gtk_icon_theme_load_icon or gtk_icon_info_load_symbolic depending on the icon, and draw it to the context with gdk_cairo_set_source_pixbuf

  • How do I get my lines subpixel thin ?. Turns out that cairo can draw lines thinner than one pixel, but for that to happen you need to place the cairo starting point in the middle of the pixel, so the drawing can be made, and look just like you want.

    /* a vertical 0.3 pixel line */
    cairo_set_line_width (cr, 0.3);
    cairo_move_to (cr, 10, 10.4);
    cairo_rel_line_to (cr, 0, 10);
    cairo_stroke (cr);
    

Finally, use whenever you need gtk_render_background, gtk_render_frame family and friends over any alternative drawing you could think of doing. These functions will get colors and border size and radius from the CSS theming engine, therefore you’re spared a bunch of work.

Processing events

How to receive and handle mouse, touch and button events. My first attempt to catching and handling event was to implement virtual method button_press and button_release, that didn’t worked. Not that easily.

For catching input events in your custom widget, you will need a GdkWindow suited for those purposes. The preferred idiom here is:

  • In your realize method, create a GdkWindow with gdk_window_new and set its type to GDK_WINDOW_CHILD, this would assure to catch the input events if they occur inside you window. After created the window, call gdk_window_show to show it. You will use gdk_window_set_user_data to relate the window with your widget.
  • In your size_allocate method, you need to place the window where it should, by using gdk_window_move_resize.
  • Now, you can implement button_press and the likes of them to handle your events.

There are virtual methods for handling mouse button presses, and a releases, but no for high-level actions as click and double-click, you need to detect those by detecting sequences of presses and releases. Nonetheless, for double-click the GdkEventButton will have the type field set to GDK_2BUTTON_PRESS which will certainly help. For key-press/release events, I have to say, I haven’t had nothing to do with those yet. So, that will be left to the next time.

Putting things together

I won’t paste the code entirely, I’ll just describe the order in which the above mentioned steps will most likely occur.

Usually, as you must have guessed by now, the first thing your widget will receive is a request for its preferred size using gtk_widget_get_preferred_width and friends. After that, the parent will follow to allocate the size, there your implementation of size_allocate will run, and then, the painting will happen. About the realize part, it will happens almost anytime, the only thing you can be sure of is: it will happens before your widget had to paint itself. One thing worth mentioning is that a widget could be allocated a bunch of times before it ever gets to the painting part.

About a full example of code, one the simplest widgets for showing all of this is GtkButton. You can read the source of in the Gtk+ tree. Here’s the header: gtkbutton.h and the source gtkbutton.c. As I said above, reaching into Gtk+ internals is the best way to learn to use the toolkit and the framework.

Conclusion

Obviously this a pretty simple post about some of the more common tasks you’ll meet with when making a custom widget. There are several more complex widgets which are far from this, but those are left for later.

Hope it helps, and if you find any mistake, please contact me immediately so I can fix it.

Comments