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 ;)

Template-GLib

One of the things necessary in an IDE is the ability to create projects from templates. This is something we’ve been lacking since the beginning, and I’m finally in a position to follow through on it.

So, templates. What is a template? We have a couple layers of templates needed. We have, at a macro level, project templates. These are a collections of files, that get tweaked based on the project options.

Then we have per-file templates. These need to go through various tweaking and can get quite detailed, based on how much you want to do for the user.

I did a quick survey of what was available to reuse, and was not entire satisfied with the options. Thankfully I like programming languages and compilers, and decided to create exactly what I was hoping to find.

The result, was written over the past few days, and is called template-glib. It will look familiar to people who’ve used other template engines. Bits and pieces had inspiration from all over the place. I rather like python’s mako template API, but not much of a fan of the implementation. I also don’t want a bidirectional run-time[1] involved.

Some people really seem to like Go’s text templating. I’ve never used it, but like any language community full of crusaders, they’ll come to your door to let you know why they love it so much. Some things will be familiar to those people.

The API is designed in such a way that parsing and expanding the template are separate operations. If you were to use it in such a fashion, the parsing operation will be much less expensive.

Expanding of templates uses a TmplScope which can have inheritance. That allows for some nifty scoping control as you work your way down. I haven’t finished this to the level I’d like, but “it works”.

You can control resolving of included files using TmplTemplateLocator. This is similar to python mako’s TemplateLookup. Works as advertised.

You can include files, like:
{{include "foo.tmpl"}}

You can cycle through certain types of objects
{{for item in my_list}}
{{item.title}}
{{end}}

You can perform various expressions, including assignment.
{{x=sqrt(10)*sqrt(20)}}

You can call into methods via GObject Introspection.
{{item.short_description("with", "params")}}

You can use expressions in conditionals.
{{if item.year > 2016}}
Current: {{item.title}}
{{else}}
Archived: {{item.title}}
{{end}}

This code is very early, in fact less than a week old, but I’ll keep pushing it forward to add the features we need in Builder. Perhaps you’ll find it useful too. I’d certainly love to see someone make a nice GNOME 3 static blog generator.

You can find the lexer here and the parser here for the expression language.

[1] Bidirectional run-time meaning that objects can live en either the run-time or outside of it, and coordination of GC must occur.

Command line tools

There is one thing that has been important to me for a long time in Builder, but I’ve been unable to solve the problem correctly until recently. And that is command line tools.

I’m not naive enough to believe that I can convince every multi-decade Emacs and Vim user to switch to a Gtk-based application. Even if our emulation of such systems are perfect, the muscle-memory of launching them from the command line is entrenched in work-flows. Because of that, I wanted to export many of our tools via command line tools.

In git, Builder now has support for an ide command line tool. The goal is for plugins to export their features via a command line tool. The added benefit is that it can provide an easier way to test core functionality of your plugin.

These aren’t all finished, but this gives you an idea of where we are going.


# list available commands
ide --list-commands

# -d allows selecting target device
ide build -j 12 -d local

# clone/bootstrap/build, prepares for contribution
ide contribute gtk+

# create a shared library project
ide create-project foo-glib --template=library --with-tests

You get the idea. None of those work as advertised, but you know it goes, that’s the plan.

Video Series

I’m nearly a month down on a branch for Builder 3.20. It’s goal is to radically simplify the process of creating plugins, and prepare for external plugins. We really wanted to create a solid plugin story before doing that and things are progressing nicely.

However, I wanted to point out I put together a short(ish) video on creating your first GObject in C. It does expect some familiarity with C. You can find the video on Youtube, or alternatively, I applaud your use of youtube-dl.

youtube-dl https://www.youtube.com/watch?v=1-_EBEr0fxI

I hope to do more of these videos (with better planning, storyboarding, and production quality) as time permits. I’d also like companion material (latex chapters or similar) if possible.

And I’ll leave you with a few screenshots to whet your appetite. All UI elements are non-final. We’ve been iterating and playing with a few ideas to see what sticks.

Editor and tab-like things in perspectives

New preferences perspectives for GNOME Builder 3.20

Builder 3.20 development underway

I’ve been busy working on the plumbing for what will become Builder 3.20. We have a really ambitious cycle ahead of us, so getting these core changes in place as soon as possible will help give us time to stabilize.

From the early design phase of Builder, we knew we wanted multi-process plugins. Easier said than done. This cycle we are starting to follow through on that design goal by making it simple for plugins to achieve this.

I just landed the multi-process plumbing and ported our Jedi auto-completion engine to use it. Previously we were using Jedi from Python threads. It would occasionally cause the main loop to stall (likely due to GIL) as we have plugins written in Python inside the UI. Now that the work is being done in a worker process, we simply receive a large GVariant of the result set from the worker process over a private GDBus connection. This allows us to allocate/free large memory chunks instead of so many small strings which is nice. There are still a lot of strings created when sizing the completion window, but we can address that later.

Since we require GDBus, and PyGObject support for GDBus is not very convenient, we ported some of Martin Pitt’s work into the Builder G-I overrides. This means you can quickly create a GDBus service in Python with a few lines of code. It’s still not ideal, but again, we can iterate now. One thing I’d like to see is the ability to use yield to wait until an asynchronous operation completes. Python Twisted has been doing this for nearly a decade, and it’s quite pleasant to use. Also, we had to go out of our way to use GVariant from Python without unwrapping the variants. We want to avoid doing that until absolutely necessary.

The next big step in multi-process is to port the clang and vala plugins to use the same plumbing. After that, we need to get the plugins to recycle when they hit various thresholds such as undue memory consumption.

I think our story with plugins is going to become pretty compelling this cycle. Being able to write a single plugin that can take advantage of multiple-processes without destroying your code is an area where we can really shine above the competition.

I’ve also started work on distributing Builder as an Xdg-App. We have some unique constraints that will help us push Xdg-App to support some very difficult corner cases. Psuedo terminal support landed last week, so the terminal now works. Although, your terminal is inside of the mount namespace, so it won’t match your host system. That might feel a bit odd at first.

Additionally, we need to come up with a good design that allows Builder to run in one xdg-app, while building/running/testing your application from another xdg-app/runtime/sdk.

As always, lots to do! If you are interested in contributing to Builder or Xdg-App, this is a great time to get started! Come join us on irc.gimp.net in #gnome-builder and we’ll find a way for you to contribute.