Builder Plugins – Part I

The time to start writing plugins for Builder is here!

While the API of LibIDE may churn a bit, now is a good time to get started on your plugin of choice. Things are easier than ever and we hope to continue trending in that direction.

We support writing plugins in a variety of languages. Currently, C, Vala, and Python 3 are all supported in Builder.You can find examples of all of these in the plugins/ directory.

We use libpeas for our plugin support. That means the process for creating a plugin in Builder is quite similar to Gedit, Rythmbox, Eog, and others.

I’m going to use Python 3 as our language of choice for examples due to the succinctness.

Create a .plugin description

The first step in our plugin is to create a plugin description file. The convention we use for this is module-name.plugin.

[Plugin]
Name=Hello World
Description=A sample hello world plugin
Authors=Example 
Copyright=Copyright © 2016 Example

Loader=python3
Module=my_plugin

The stuff at the top is your basic title, description, and attribution.

The two lines at the bottom tell us what plugin loader to use, and the name of the module to load. In this case, we want to use the python3 loader. Omitting this line would default to the C loader, which can load a shared library using an equivalent to dlopen().

The Module, in this case, is our Python 3 module. If we were doing this in C or Vala, it would be the name of the shared library, omitting the “lib” prefix and “.so” suffix.

Create a plugin Python module

Lets create our python module now. The following will create an IdeApplicationAddin. An IdeApplicationAddin allows you to extend the IdeApplication object. There is only ever one IdeApplication per-process, so this is an excellent place to put “singleton” type code.

# my_plugin.py

from gi.repository import GObject
from gi.repository import Ide

# Ide.ApplicationAddin interface requires a GObject, so we
# inherit from GObject.Object. Ide.ApplicationAddin is an
# interface and it has two override'able methods. do_load()
# and do_unload(). Each take the application instance as a
# parameter.
class MyAppAddin(GObject.Object, Ide.ApplicationAddin):
    def do_load(self, app):
        print("Hello, World!")

        # If you'd like to access this instance like a
        # singleton from other extensions in your plugin,
        # then you might want to give yourself access to
        # the MyAppAddin instance.
        #
        # Doing the following will allow you to use
        # "MyAppAddin.instance" as your singleton.
        MyAppAddin.instance = self

    def do_unload(self, app):
        # Unload our singleton
        MyAppAddin.instance = None

That’s it! Put my_plugin.plugin and my_plugin.py in ~/.local/share/gnome-builder/plugins and restart Builder.

You should see your plugin in the Extensions section of the Preferences perspective.

hello-world

You can imagine lots of things you might want to do only once per-process. Setup a DBus service, manage compiler tooling, provide access to an external web service, you name it.

In my next post, we’ll take things a bit deeper now that we have the machanics of creating a new plugin.

EggStateMachine

So the topic of the day is EggStateMachine. This is a handy class for managing various states in your UI that might have complex transitions. One example might be the GNOME 3 style search pattern. The search pattern has a few states.

  • Inital state which contains the content overview
  • The active search state, including type-ahead search
  • Object selection state
  • Object selection state with a selection

When we switch between the above states, we might need to:

  • Toggle search and object-selection toggle buttons
  • Toggle search revealer visibility
  • Toggle action bar visibility
  • Toggle action bar button sensitivity
  • Show checkbuttons on content overview
  • Alter headerbar CSS classes
  • Toggle visibility of close button on header bar
  • Toggle visibility of cancel button on header bar

You get the idea. Getting everything right is critical to the UX, and that becomes unpleasant code rather quickly.

EggStateMachine allows you to do much of above all from your Gtk+ .ui template file. Additionally, you can use the native C API if using .ui templates is not your thing. For those people, you can jump to the header file to get the idea, link at the bottom.

For the rest of you, take a look at this snippet.

<object class="EggStateMachine">
 <property name="state">browse</property>
 <states>

  <state name="browse">
   <object id="titlebar">
    <property name="show-close-button">true</property>
   </object>
   <object id="cancel_button">
    <property name="visible">false</property>
   </object>
   <object id="action_bar">
    <property name="visible">false</property>
   </object>
  </state>

  <state name="selection">
   <object id="titlebar">
    <property name="show-close-button">false</property>
    <style>
     <class name="selection-mode"/>
    </style>
   </object>
  </state>

 </states>
</object>

This is just a snippet, and shows two states. The “browse” state and the “selection” state. You can see that you can reference other objects in your .ui definition using the <object id=""> syntax. While I didn’t show it here, you can use the binding syntax to bind properties only in a given state.

You can also define style-classes in the state. When the state transitions, the style-class will automatically be added or removed.

To perform a state transition, set the EggStateMachine:state property. You can also create an action for this using egg_state_machine_create_action(). If you add that action to your widget hierarchy (such as win.state), then you can remove the need to perform complex toggles in your application code as well. Take a look at the following snippet.

<object class="GtkToggleButton" id="selection_button">
  <property name="action-name">win.state</property>
  <property name="action-target">'selection'</property>
  <child>
   <object class="GtkImage">
    <property name="visible">true</property>
    <property name="icon-name">object-select-symbolic</property>
   </object>
  </child>
</object>

To add the state action to your widget hierarchy, do something like:

GAction *action;

action = egg_state_machine_create_action (state_machine, "state");
g_action_map_add_action (G_ACTION_MAP (my_window), action);
g_object_unref (action);

Now the button will be toggled when the state matches "selection".

And for practical use, see Builder’s greeter implementation.

EggAnimation

Here is a nifty trick I’ve had up my sleeve since the Gtk 2.x days (wow, over half a decade ago!). I’ve used this on a bunch of projects over the years, and it always gets a few smiles. It has a very similar API to the early days of Clutter, where it’s inpsiration came from. I should preface this with: If you need very fast, GPU optimized graphics, this is not what you want. However, for automating property changes, it is very handy.

Let’s start simple by transitioning the opacity of a widget from 0 (transparent) to 1 (opaque).

gtk_widget_set_opacity (widget, 0.0);
egg_object_animate (widget,
                    EGG_ANIMATION_LINEAR,
                    1000, /* duration in msec */
                    NULL, /* frame clock, NULL for default */
                    "opacity", 1.0, /* key/value properties */
                    NULL);

This is fairly contrived, because you almost certainly want to do the above with CSS transitions.

Now consider scrolling to the bottom of a GtkScrolledWindow. The GtkAdjustment is not a widget, and therefore does not have a frame-clock. So we will pass the frame-clock of the scrolled window instead.

GdkFrameClock *frame_clock;
GtkAdjustment *v_adjustment;
gdouble upper;
gdouble page_size;
gdouble value;

frame_clock = gtk_widget_get_frame_clock (scroller);
v_adjustment = gtk_scrollable_get_vadjustment (scroller);
upper = gtk_adjustment_get_upper (v_adjustment);
page_size = gtk_adjustment_get_page_size (v_adjustment);
value = MAX (0.0, (upper - page_size));

egg_object_animate (v_adjustment,
                    EGG_ANIMATION_EASE_IN_OUT_QUAD,
                    1000,
                    frame_clock,
                    "value", value,
                    NULL);

egg_object_animate() returns a weak pointer to the EggAnimation. This is safe since the animation won’t start until the next iteration of the main loop. If you want to hold on to it, either use g_object_weak_ref() or g_object_ref. I prefer the weak ref (via g_object_add_weak_pointer()) so it is easy for me to cancel in-flight animations with egg_animation_stop().

Here is an example of how we use EggAnimation in Builder to make the “yank” operation in Vim mode more visible.

For many things, you’ll be able to just use CSS transitions. And you should definitely check if that is an option before using EggAnimation.

But that said, it’s used all over in Builder. If I did my job well, you barely even notice. And of course, for the animation hating crowed, GtkSettings:gtk-enable-animations is respected.

EggScrolledWindow

A common pattern you see across software targeting a recent Gtk+ stack requires the use of GtkPopover. Choosing the right default-size for that popover is an exercise in futility.

Really, what you want is for the popover to shrink and grow in size based on the child, but only up to a certain point. EggScrolledWindow makes this relatively quick and easy for you to do.

Simply replace your use of GtkScrolledWinodw with EggScrolledWindow (in a template .ui file I hope) and set the max-content-width and max-content-height properties.

<object class="EggScrolledWindow">
  <property name="visible">true</property>
  <property name="min-content-height">20</property>
  <property name="max-content-height">500</property>
  <property name="min-content-width">100</property>
  <property name="max-content-width">400</property>
  <child>
    <object ...>
    </object>
  </child>
</object>

Update: Patrick Griffis ported EggScrolledWindow to Python, which can be found found here.

EggSlider

Today we’ll cover a playful widget. You probably won’t find many great uses for this widget, but then again, maybe you will.

You can use this widget to “hide” things on the top, left, right, or bottom of a widget. The contents will slide in based on the EggSlider:position property. If someone manages to tie this together with GtkGesture, you could have something like “hidden menus” found in some Android and iOS applications.

EggSettingsSandwich

GSettings is awesome, no doubt about it. Another uncommon thing we need in Builder are “layered settings”. We may have user “global settings” which are different than configured “project settings”.

In comes in the form of a wonderfully named EggSettingsSandwich. It let’s you stack GSettings atop one another, and read from the highest priority layer with modifications. Change notification works by tracking underlying changes and generating a “compiled” state using a memory-backed GSettings.

Your layer cake can be arbitrarily deep. We use g_settings_get_user_value() to determine if the value has changed at that layer.

EggSettingsFlagAction

Using object bindings with GSettings is generally a snap. However, there are few places where it breaks down. One in particular for us in Builder was with “Flags Types”. A flags type can be thought of as an integer where a bit is either on or off, indicating a feature. You can think of them as a series of booleans.

We use this for the “Draw Spaces” feature in Builder. The data type in memory is a GTypeFlags (GtkSourceDrawSpacesFlags), and the underlying GSettingsSchema reflects that. However, in the schema you define a nickname. The setting is really an array of nicknames, indicating “on”. If the nickname is absent, the flag can be thought of as off. (Each name does map an integer value, which can simplify getting the value as a 32-bit uint). Handy.

We want to bind each item in the flags individually, which to the best of my knowledge cannot be done out of the box with GSettings. So we added a new GAction which can be used to bridge the gap between GtkActionable and GSettings. Simply set the properties to indicate which flag the action should toggle.

EggSignalGroup and EggBindingGroup

Have you ever had to deal with lots of GBinding or signal connection (and therefore disconnections) on indirect objects? A classic example for this is the GtkTextView:buffer GObject property.

It’s not uncommon to find vastly broken code when it comes to this. People will g_signal_connect() and never disconnect. Slightly better is to use g_signal_connect_object()[1]. However even that requires that one of the objects be finalized to work properly.

EggSignalGroup allows you to connect to a bunch of signals on a single target object as a group. You can connect and disconnect them simply by setting the EggSignalGroup:target property. I find this convenient because I can setup the EggSignalGroup in my instance init function, and simply set the target when it becomes available. You can even bind it using g_object_bind_property() for even less application code.

EggSignalGroup *group;

group = egg_signal_group_new (GTK_TYPE_TEXT_VIEW);
egg_signal_group_connect_object (group,
                                 "key-press-event",
                                 G_CALLBACK (press_event),
                                 self,
                                 G_CONNECT_SWAPPED);
egg_signal_group_connect_object (group,
                                 "key-release-event",
                                 G_CALLBACK (release_event),
                                 self,
                                 G_CONNECT_SWAPPED);

/* ... later on */
egg_signal_group_set_target (group, text_view);

/* ... and to disconnect */
g_clear_object (&group);
/* or */
egg_signal_group_set_target (group, NULL);

EggBindingGroup is similar in spirit, but for GBinding. You can create a series of delayed bindings and apply them as a set. Simply set the EggBindingGroup:source property to the GObject instance you would like to bind.

EggBindingGroup *group;

group = egg_binding_group_new ();
egg_binding_group_bind (group, "foo",
                        target, "foo",
                        G_BINDING_SYNC_CREATE);
egg_binding_group_bind (group, "bar",
                        target, "bar",
                        G_BINDING_SYNC_CREATE);

/* later on */
egg_binding_group_set_source (group, my_obj);

[1] g_signal_connect_object() used to be broken, but should work as expected.

GMenu Merging

One of the long missed features of GtkUIManager (at least from a complex application perspective) is menu merging. This is something that gets heavily used in “plug-in oriented” software. And as you might expect, a large code-base will often migrate into this direction simply out of code hygiene.

I implemented something akin to this in Builder so that plugins could easily merge into the application GMenus used by the application. You can find the header and source at egg-menu-manager.h and egg-menu-manager.c respectively.

To make things easier for plug-in authors, we have a feature similar to the automatic resources in GtkApplication. Just place your menus.ui at /org/gnome/builder/plugins/:plugin-name/gtk/menus.ui and it will be automatically loaded and unloaded with your plugin.

Build Panel

Did you know that Builder has a build panel finally? That’s right, you can actually do what the name says now. Of course, we only have one IdeBuildSystem implementation at the moment (autotools), but if you implement that interface, you too can have your project building.

Note that you can write build system implementations in Python or Vala now. That should lower the barrier for contributions quite a bit.

Build Panel

With build output

Never mind the CSS errors in the theme, those will be fixed soon enough ;)