It is possible to port now app written in GTK3, Gtk-rs and Glade to GTK4?

Hi,
Not so long ago I created an app using Rust and GTK3 via Gtk-rs.
I used Glade 3.38 to simplify GUI creation(I really hate creating GUI with code).

I now want to port my app to GTK4, because in future I won’t have so much free time as now.

Is Gtk4-rs able to run a little more advanced project or needs more time to mature?
Which app I can use to graphically edit .ui or .glade files? Looks that Glade won’t be updated to support GTK4.
How to upgrade .ui or .glade files to GTK4?
It is possible to install GTK4 on Ubuntu 18.04/20.04 on Github CI?

There is a tool to help: https://developer.gnome.org/gtk4/stable/gtk4-builder-tool.html

$ gtk4-builder-tool simplify --3to4 my.glade

but you will probably need to complete the work by editing the .ui yourself.

Is Gtk4-rs able to run a little more advanced project or needs more time to mature?

GTK 4 bindings work fine and probably more complete than the GTK 3 ones. Especially for the subclassing part which allows you to create your own custom widgets.

Which app I can use to graphically edit .ui or .glade files? Looks that Glade won’t be updated to support GTK4.

Note .glade files are GTK 2 era, you should use .ui as an extension. Glade doesn’t support GTK 4 though, there are plans to provide some designer app, but no idea of when there would be one.

How to upgrade .ui or .glade files to GTK4?

As you generated the original files using Glade, I think the best is to modify them manually as Glade generates a ton of not needed stuff. Most of that code can probably be removed. Especially that GTK 4 has saner defaults such as widgets being visible by default so you don’t need to specify <property name="visible">True</property> for all your widgets.

It is possible to install GTK4 on Ubuntu 18.04/20.04 on Github CI?

That’s a question Ubuntu people should answer, give it a try. You might need a newer glib/pango which you definitely don’t have in Ubuntu 18.04, no idea about 20.04 though.

1 Like

That is really interesting. Can you tell us more about how subclassing of GObjects is handled in Rust? For Nim we use proxy objects and toggle references. That makes Widgets look like ordinary Nim objects, the user can add additional fields and attributes like it is done in Nim. But we can not use the low level gobject stuff, and proxy objects and toggle references create some overhead of course. So I wonder if there may exist a better solution. (Nim supports destructors, so foreign entities can be destroyed automatically when they go out of scope.)

In the Rust bindings when using existing GObjects, there are no proxy objects or similar like in many other bindings. Instead they’re used as is and integrated with the Rust memory management mechanisms (RAII-style like in C++ for example): a glib::Object in Rust is literally just a pointer like in C (and the same one) and via the Rust Clone (mapping to g_object_ref()) and Drop (mapping to g_object_unref()) traits the memory is handled deterministically and the same as for any other Rust code. If you look at the assembly that is generated for Rust GObject code, it’s very close to what the corresponding C GObject code would look like. This also has the big advantage that it composes well with other languages used in the same process, while toggle references easily make it impossible to have more than one language in the process using them.

For actually creating GObject subclasses, I wrote an very explicit overview of that here some years ago. It’s again basically the same thing you would do in C, there’s basically no runtime overhead compared to C and it looks all the same from a C point of view. That has the big advantage that you can easily write C-compatible GObject libraries in Rust and have them used from any other language with GObject-Introspection support.

Now all that is super verbose, so with a lot of Rust type system trickery and some usage of macros the whole thing looks much simpler nowadays but under the hood still compiles to the same assembly. You can find an example of the current code you’d write in Rust here and explained in great detail here in @julian 's great documentation.

How this would map to Nim, I don’t know. Whenever a GC is involved mapping the behaviour of that nicely to GObject becomes complex but I guess mapping to the Nim reference counting memory management strategies should be nicely possible without having to deal with toggle references. As long as Nim provides you with the necessary API for doing that.

For creating subclasses you’ll have to find a way to map the way how it’s done in C (or what I describe in my blog post above for Rust) to Nim language constructs. I’m sure it would be possible to just do exactly the same as in C but that would be very verbose and nobody would like to write code like that. Based on my blog post, someone did the same for Go for example and with some additional work on top it seems usable in the go-gst GStreamer bindings. The Python, JavaScript, C#, etc. bindings also manage it in a similar way.

The main challenge is to map the whole thing to the target language in a way that it doesn’t feel too alien, isn’t too verbose and developers would like to make use of it. Making it work the same way as in C is usually not very difficult but then people could as well directly write C so that’s kind of defeating the point of using bindings.

5 Likes