Trying out Vala

If you have seen my Codeberg profile you'll no doubt notice that most of the active projects are written in Rust, with a few outliers in Zig. There's a smattering of other code, but I can't deny that Rust is my happy place.

Nevertheless, I like to try out new things from time to time. I've known about Vala since about circa 2006-7. Around that time I was a frequent user of Puppy Linux and a sometimes contributor to it (I shepherded the first community edition release). The project lead was an Australian by the name of Barry Kauler who always seemed to be on the lookout for cool but lightweight projects that would make a good value add. The Vala and Genie languages appeared on his blog around that time, and a few small bits and bobs got written for the distro in Genie around that time.

Vala is a unique language. Some might even hesitate to call it a full programming language, though I would disagree, on the basis that it exists almost entirely to provide an easier syntax to program around GLib and GObject code (the low level object oriented bits of the Gnome stack). The syntax has been kept as close as possible to C# and I imagine if you have experience with any C-like language you will find it remarkably easy to get started writing Vala. The compiler transpiles the code to C and then uses the system compiler from there to generate native code.

Genie can be thought of as an alternate syntax which looks quite a lot like python. I'm more comfortable with my curly braces and semicolons so I'm not going to go into any more detail about Genie, other than to mention the really cool feature that the compiler will accept either syntax, and you can mix some Genie files and some Vala files in the same project pretty much seamlessly.

Forget the tutorials, let's just dive in..

At this point I've coded in enough languages that with rare exceptions they all seem to be the same in a number of ways. You pretty much always have similar control flow statements, although you might see match instead of switch. There's a difference between Object Oriented, Procedural or Functional, but in most cases the language can be bent a bit to fit your style. Hence I don't really follow along to tutorials anymore if I want to explore a new language. Instead I just start coding something and keep the docs and tutorials open for reference.

So anyway, about two weeks ago, after seeing a few projects on Fedi that were wrritten in Vala, I suddenly remembered that I've never looked at it in a more than cursory way, and after a quick look at the homepage I came to the conclusion that this might, in fact, be an absolutely great fit for writing applications using Gtk+. So I thought about my programming bucket list (I'm sure that you have one too, dontcha?) and thought, well, even though I'm a Vim guy, I bet I could knock together a text editor using GtkSourceView real quick. And it turned out that yes, I could knock it out pretty fast. I had the basic functionality of a text editor with tabs working in about two days of part time hacking. Two weeks later and here I am wrriting about it using said editor.

For now I'm calling it Vapad. I may very well change the name before doing a 1.0 release, because yeah, vala-pad, vapad, not very original. Still, it is close to Vaapad, which was the form of lightsaber combat employed by Mace Windu in Star Wars. If something in a project can refer to Samuel L Jackson, then it should refer to Samuel L Jackson. Even if it's a big stretch.

Anyway. The idea was this: what would the old Leafpad editor look like if it were written today instead of a couple decades ago? I abused a lot of shell scripts and html using Leafpad, so I'm a bit nostalgic. Sometimes you just want a really fast and simple editor to view a readme file when you click on it in a filemanager, rather than an IDE. Hell, with all the extensions that I have loaded in NeoVim it's basically an IDE. At least, that was the starting point. I took a contribution from another guy along the way who suggested using LibAdwaita in order to send user facing notifications using the AdwToastOverlay widget. And then from there I decided that if it's linking to LibAdwaita I might as well go all in and build my first app that officially targets Gnome. So it's a bit of a paradox, but I think it still fits very well into the original goals.

Leafpad existed before tabs became expected and never managed to aquire them. It had the ubiquitous menu bar up top with File, Edit, Search and the like. There was no preferences dialog, but there were a few toggles in the menus to turn features on and off. No syntax highlighting either, but I tend to think that with modern hardware and better wrritten library code the program might have included it. Back then, syntax highlighting definitely was a performance compromise. Today, not so much.

So since we're targeting Gnome, Vapad uses a header bar and a hamburger menu rather than a menu bar. I'm sure that Leafpad would have tabs if it were written today because it's just expected, so Vapad has tabs. There are Open and Close buttons in the headerbar to save menu space along with the new tab button. Syntax highlighting is on by default, as are a number of other comfort features that I see no logical reason why anyone would want to turn off, like automatic indentation and line numbering.

Like Leafpad, there is no preferences window. It required taking the time to do more of a deap dive into Gtk4's menu and actions subsystems, but by using Vala I was able to follow along pretty closely to the C documentation and found the whole experience pretty easy and rewarding.

You also get expected niceties like search and replace, and replace can be in the context of the current document, all open documents, or the current selection. Plus, the search has completions which are populated by your past search terms (per session - this one doesn't persist).

Partway into this I realized that the application was looking quite a lot like Gnome Text Editor. Alas, I think that's inevitable when you're trying to follow Gnome's HIG. There are of course differences, some of which are fairly substantial, but in the end I just embraced it and even looked at the code to figure out how to pull off certain things like the theme switcher that appears at the top of the menu in a lot of recent Gnome apps including TextEditor.

So what are the differences between Vapad and TextEditor, that makes it a viable project in it's own right? Vapad has a lot less user facing settings. No preferences dialog, remember? I didn't incorporate the little mini-map that SourceView allows you to place along the right margin. But I did add Vi emulation via a checkbox in the menu. It's provided as part of GtkSourceView, so why not have it as an option? Anyway, I think the interface is just about as clean as I could make it, the defaults are sane, and you can basically just fire it up and start writing without giving the interface much thought. It's truly a get out of your way sort of experience.

What's the verdict on Vala then?

I can confidently say that I can now write productively in Vala. I've never picked up a language this fast before actually. Part of that might be my increasing knowledge overall, but I'm convinced that a major part is that Vala is just a near perfect match for how Gtk+ works. It's definitely a way better experience than writing for Gtk+ using C.

Subclassing widgets and adding your own properties, signals and actions is much more straightforward than in Rust. Some of this stuff I can't really even do in Zig yet because a few things that you really need from the C interface are there in the form of macros, which Zig's C translation isn't yet completely reliable at doing.

Here's one example of doing something a bit more involved using Gtk+. There needs to be a way to manually set the syntax highlighting language if the library is unable to correctly guess it when opening a file. To that end, I wanted to add a submenu to the right-click context menu for each View widget. We need a custom property for our Vapad.Tab class, and an action which binds to that property (a GLib PropertyAction). And then we need to create the submenu, with each entry hooking to the custom action and passing it a parameter (a string representing the chosen language).

// tab.vala
namespace Vapad {
    public class Tab : Box {
        public Box lbox;
        public Label label;
        public Button close_button;
        public View sourceview;
        public GLib.File? file;
        public GtkSource.File? sourcefile;
        private Gtk.EventController? controller;
        private Gtk.Box cmd_bar;
        private Gtk.Label cmd_bar_txt;
        private Gtk.Label cmd_txt;
        public signal void file_saved (string name);
        public string syntax_language { get; set; }    // <= This part here

That last line is the custom property. Yeah, that's easy. It's just a class member where it's specified that it can be get and/or set.

        // construct is where we do some initialization for the class instance
        construct {
            // Some other initialization omitted
            
            // Make sure that variable is initialized and not null
            this.syntax_language = "C";
            // Create an action group, so that we can bind actions to the Tab
            var tabgroup = new GLib.SimpleActionGroup ();
            this.insert_action_group ("tab", tabgroup);
            // Create the PropertyAction and add it to the group
            var set_lang = new GLib.PropertyAction ("set_lang", this, "syntax_language");
            tabgroup.add_action (set_lang);
            // Make sure the callback is run when the action is activated
            set_lang.notify.connect (set_language);
        }

        public void load_file (GLib.File f) {
            // Most of this function omitted, here's the relevant parts
            // We're getting the context menu here and calling another method
            // to add our languages menu
            var extra_menu = this.sourceview.get_extra_menu ();
            if (extra_menu != null) {
                this.set_lang_menu ((GLib.Menu)extra_menu);
            }
        }

        public void set_lang_menu (GLib.Menu model) {
            // Get the list of available syntaxes
            var manager = new GtkSource.LanguageManager ();
            var languages = manager.get_language_ids ();
            // Create our submenu
            var menu = new GLib.Menu ();
            // loop through the array and add an entry for each language
            foreach (string id in languages) {
                menu.append (id, @"tab.set_lang::$id");
            }
            // Insert the submenu into the context menu
            model.insert_submenu (2, _("Language"), menu);
        }

        // Here we actually set the language whenever the custom
        // property gets tweaked.
        private void set_language () {
            var manager = new GtkSource.LanguageManager ();
            var language = manager.get_language (syntax_language);
            var buffer = (GtkSource.Buffer)this.sourceview.buffer;
            buffer.set_language (language);
        }

The result is that we get a submenu attached to our context menu, with a list of radio button entries for each syntax language. Gtk+ knows that since each of those entries is for the same action, they should be displayed as radio buttons. If it were a single boolean property it would be displayed as a checkbutton.

Now, this can all be done in Rust. But it was a lot easier to figure out when you don't have the friction of trying to fit an object oriented design into Rust. I'll actually be much more confident attempting these sorts of things in Rust now after doing them in Vala though.

But it isn't like I'm ready to just ditch Rust in favor of Vala. Far from it actually. For one thing, I would only reach for Vala if the project was primarily one of wrapping a gui interface around some good library code. GLib has some good string handling code, but I'm not going to attempt any kind of logic heavy programming in Vala. Vapad is pretty much just showing off the features that are already present in GtkSourceView, so frankly there's no need of a heavy duty programming language.

Also, there were a number of times where I got things compiling and then at runtime everything misbehaved. In almost all of those cases, I'm sure that RustC would have objected to what I was doing and politely given me some tips on how to do it right. Vala left me in charge of figuring out what went wrong. So I think you're making a tradeoff here. What you get from Vala is rapid development speed and low mental overhead. What you get from Rust is much more confidence that you've written it correctly the first time. I imagine that given enough time with Vala that might be a net win, but I can't say with any confidence that I'd be more productive overall than I am with Rust, because Rust isn't going to let me waste my time writing the wrong code.

But by a different measure I'd call Vala a huge win any day. Vala uses reference counting for all objects by default, so in comparison with writing an equivalent program in C we're talking orders of magnitude less mental overhead along with a lot less boilerplate and better syntax.