💾 Archived View for gemini.hitchhiker-linux.org › gemlog › some_thoughts_on_vala.gmi captured on 2024-06-16 at 12:27:54. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-06-14)
-=-=-=-=-=-=-
I've been aware of Vala for a pretty long time now. It was first brought to my attention back when I regularly used Puppy Linux, as Barry Kauler had begun using and promoting it's sister language, Genie, for some small utilities in that distro. At the time, I wasn't confident enough in my own ability to have tried to progress much beyond shell scripting, so I never got around to trying it out myself. Recently it's been on my radar again, after seeing a number of interesting projects written in Vala (including a couple of Gemini browsers). I figured I really should investigate a bit more.
To understand the point of Vala, it's helpful to dig into some of the underpinnings of the Gnome stack. Glib is a fairly broad spectrum base library which provides a lot of data structures and features which are taken for granted in most modern languages, but not provided by Libc. One of the more influential components of Glib is the GObject system, which provides an object oriented framework for C. However, as C itself was not designed with object oriented programming in mind, using the interface from C is pretty clunky at times and involves quite a lot of boilerplate. This is where Vala comes in.
Vala is a C# inspired language based around GObject, which provides an interface to the Gnome stack that is much simpler than the C interface and was designed with Object Oriented concepts right from the beginning. It compiles down to C code, which is then compiled to native binaries, and does not impose a runtime of it's own. What that means is that Vala programs run at pretty much the same speed as their equivalent C program. I'm not a huge believer in Object Oriented programming for every type of program one might write, but it does match pretty well with a traditional retained mode, event loop driven gui. And Vala seems incredibly easy to pick up if you already have some programming experience.
Genie is, as mentioned previously, sort of a sister language to Vala. Both languages are understood by the Vala compiler and in fact the two languages can be mixed within the same project. The difference is that where Vala's syntax is purposely as close to C# as possible, Genie took inspiration from Python. In genie, instead of curly braces and semicolons to denote statements and blocks you use indentation and whitespace. Pretty cool, if you're into Python and want to slide into a compiled language with simlilar syntax. That's not me though. When I got serious about programming I started with C, and moved from there to Rust and Zig. So I'll gladly stick with curly braces and semicolons now because it looks "right" to my eye.
Installing the Vala compiler, valac, on Arch was as simple as `pacman -S valac`. Most vala projects are also now using Meson and Ninja, which are both widely available in package repositories. If you should happen to need (or want) to bootstrap the compiler from source, it builds in a couple of minutes on a 4-core 7th gen I5. Definitely not a pig like Gcc or Clang (or Rustc or Zig). There is also a vala-language-server, which was also in the Arch repositoies. If you're using VScode there's a plugin, and Gnome Builder seems to also have very good Vala support. I use Neovim, so alongside the lsp support I added a syntax highlighting plugin. I'm using Packer, but other plugin managers should be similar.
-- init.lua for Neovim (excerpts) require('packer').startup(function(use) -- omitting my other plugins.. use 'arrufat/vala.vim' end) -- further down, add 'vala_ls' to list of language servers -- As you can see, I have a number of other languages configured here local servers = { 'clangd', 'rust_analyzer', 'zls', 'fortls', 'pylsp', 'vala_ls' }
Obviously, you can go much more minimal if you like. Neovim with lsp has me spoiled and seems like a great compromise between an ide and a traditional modal editor.
There are various tutorials online. I can't really speak to how good they may or may not be, because after a cursory look at a few code examples I just dove into writing a real program. In my previous post, I complained about the amount boilerplate that Gnome builder thinks is necessary to begin a project, but I decided to go with the project skeleton anyway just as a learning exercise, since it would give me some code to refer back to. After all, this is going to most likely be a throwaway project. Starting with a plain "Hello World!" window using Gtk4, I thought I'd use gtksourceview and create a tabbed text editor.
Eva was my first experience with creating custom widgets in Gtk+ by subclassing an existing widget. This is a fairly complicated thing to do in Rust with a good bit of boilerplate and a lot of moving parts which are easy to get wrong. It took a lot of trail and error before I got comfortable with doing it. In contrast, when I went to create a custom "Tab" widget in vala, subclassed from a GtkBox, it was really straightforward.
using Gtk; using GtkSource; namespace Vapad { public class Tab : Box { public Box lbox; public Label label; public Button close_button; public View sourceview; public Tab () { create_widgets (); } private void create_widgets () { this.lbox = new Box (Orientation.HORIZONTAL, 5); this.label = new Label ("New file"); lbox.append (this.label); this.close_button = new Button (); this.close_button.set_has_frame (false); lbox.append (this.close_button); var image = new Image.from_icon_name ("window-close-symbolic"); this.close_button.set_child (image); var scroller = new ScrolledWindow (); this.append (scroller); this.sourceview = new View(); scroller.set_child (this.sourceview); scroller.set_hexpand (true); } } }
That's crazy simple compared with the equivalent Rust code. I'm having to retrain my brain to use "this" instead of "self" anyway since I've been using php for a paid project, but the syntax overall is really quite easy to understand and pick up. This is a composite widget, where we have a GtkSourceView widget packed into a ScrolledWindow packed into a GtkBox. The GtkBox is going to be the widget that makes up each tab, and the other Box "lbox" will be it's label, which has an internal label and close button. The project skeleton gave us a Window widget which was subclassed from a GtkApplicationWindow using an xml ui file to make a composite template. I removed the "Hello World!" label from the window and replaced it with a GtkNotebook in the ui file, and then added a "New Tab" button to the ui file as the notebook's action widget. Here's the relevant section of the ui file.
<child> <object class="GtkNotebook" id="notebook"> <property name="hexpand">1</property> <property name="vexpand">1</property> <property name="enable-popup">1</property> <property name="scrollable">1</property> <property name="show-tabs">1</property> <child type="action-end"> <object id="new_tab_button" class="GtkButton"> <property name="tooltip-text">Open a new tab</property> <property name="has-frame">0</property> <property name="action-name">win.new_file</property> <child> <object class="GtkImage"> <property name="icon-name">tab-new-symbolic</property> </object> </child> </object> </child> </object> </child>
Note that the "new_tab_button" has a named action already associated with it. Gtk's action system is nice and simple once you understand it. Like most things open source, it could be better documented. But basically, we create an action with that name, associate it with with the window and add a keyboard shortcut. There is then no need to retain a reference to the button in our window subclass, so that we can connect to the "clicked" signal, because the action will already be associated with button. As a bonus, we can add that same named action to a menu item and the menu item will then launch the action, and we'll have the associated shortcut displayed next to the menu item. You get a lot for just a little code. I absolutely hate XML and wish that Gtk+ used some other kind of declarative format for ui definitions, but it's this way because Gtk+ is a product of the late 90's and early 2000's and everything had to be XML around that time.
Anyway, let's look at our Window subclass as it sits currently.
namespace Vapad { [GtkTemplate (ui = "/org/hitchhiker_linux/vapad/window.ui")] public class Window : Gtk.ApplicationWindow { [GtkChild] private unowned Gtk.Notebook notebook; public Window (Gtk.Application app) { Object (application: app); } construct { ActionEntry[] actions = { { "new_file", this.new_page }, { "close_file", this.close_page }, }; this.add_action_entries (actions, this); this.notebook.page_removed.connect ( () => { if (this.notebook.get_n_pages () == 0) { this.close (); } }); } public void new_page () { var tab = new Vapad.Tab (); this.notebook.append_page(tab, tab.lbox); tab.close_button.clicked.connect ( () => { this.notebook.remove_page (this.notebook.page_num (tab)); }); } public void close_page () { var num = this.notebook.get_current_page (); this.notebook.remove_page (num); } } }
It's kind of funny to me that the "close_page" function turned out to be so short in Vala. In Rust, getting the current page is a fallible operation (because maybe there is no tab) and Rust forces you to deal with it. In reality that isn't going to happen because I'm connecting to the "page-removed" signal of the notebook and closing the window when the last tab closes. I could actually turn the Vala version into a one liner.
Similarly, setting up actions is much less verbose than in Rust. The Rust interface to Gtk+ quite heavily relies on macros for subclassing and related tasks, and it's overall not something that matches up well with Rust syntax, leaving gtk-rs programs looking very un-idiomatic looking. My Zig bindings to Gtk3 used in Zterm don't even allow you to do a lot of these things, greatly complicating the code and making it much more verbose to accomplish what are pretty simple tasks in Vala. Vala is showing here just how purpose built it is for this domain.
The GtkApplication that was set up via the project skeleton is also subclassed from GtkApplication, although I'm not sure how much gain we're getting from that. I'm not going to show that file because this post is already pretty heavy on code. The only things that I had to add to it were setting up the keyboard accels for our actions and adding a first initial tab when the program is launched, by calling the "new_page" method on our instance of "Vapad.Window" which is the initial window.
As it sits this editor isn't yet very useful. You can open new buffers and close them, and write to your heart's content. But you can't save a buffer to a file or open any files. The editor isn't taking advantage of any of the features that you get from GtkSourceView, like auto-indenting or syntax highlighting. Nevertheless, in just a couple hours I managed to go from a "Hello World!" project skeleton to a dynamic tabbed interface, in a language that I've never used before. I imagine once I'm up to speed a little better I could be very productive in Vala. I like it. I'm not ready to switch over to coding in Vala full time, but I'm sold on adding it to my arsenal.
I think I'm going to take this just a bit further on a "for fun" basis and add the ability to open and save files, as well as adding syntax highlighting, line numbers and auto-indentation. I may even throw the code up on Codeberg once I consider it functional enough. But I'm not going to take it much further than that because it's not really a program that I would get much use out of myself, since I always seem to fall back to terminal based editors. But it will be a good way to get a little bit more comfortable with Vala and to get to know the Gnome stack a little better. Hopefully someone else finds this interesting as well.
All content for this site is licensed as CC BY-SA.
© 2022 by JeanG3nie