Idiomatic compile-time Builder

Hello,
I recently started using gtk4 and I really like it.
I would like to discuss the Builder struct in Rust.
Consider this example:

let ui_src = include_str!("gtk_builder.ui");
let builder = Builder::from_string(ui_src);
let window: ApplicationWindow = builder.object("window").expect("Couldn't get window");

I understand this. The UI file is directly included in the code at compile-time. When the program starts, it parses the XML file and returns a builder. If you want to access an object you call .object() and it will return a Result.
However, what I don’t understand is why we don’t have a compile-time Builder. Rust has the capabilities to parse the XML file at compile-time and generate the code to create the UI.
We could have something like this

let ui_src = include_str!("gtk_builder.ui");
let ui = parse_ui!(ui_src);
let window = ui.window;

This would be much better. There is no Result when trying to access the window, because it will be there. If an object is removed from the file and the programmer tries to access is, the code won’t compile anymore.

Is this feature being implemented? Has anyone talked about this? Do we already have this and I’m just dumb.

Thanks in advance

From what I understand is that it is because of GTK is still in written in C and already compiled. The rust bindings just allow to do stuff from rust and build GUI’s in GTK. So, even if the string is compiled into code directly, it would still need to be processed by GTK builder (which is already compiled) at runtime. I may be wrong in my explaination.

I don’t see any problem with the GTK builder being written in C? You should be able to call it at compile time to generate the rust code

You can use meson to parse and compile all resources (including images) into a single file at compile time from a XML file that specifies all the resources that you want:

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/me/name/appname">
    <file preprocess="xml-stripblanks">window.ui</file>
    <file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
  </gresource>
</gresources>

Then include in meson

gnome = import('gnome')

gnome.compile_resources('appname',
  'appname.gresource.xml',
  gresource_bundle: true,
  install: true,
  install_dir: pkgdatadir,
)

And then load it using Gio.Resource at runtime:

let resources = gio::Resource::load(PKGDATADIR.to_owned() + "/appname.gresource")
        .expect("Could not load resources");
gio::resources_register(&resources);

Use Gnome Builder and create a new rust GUI app and it will be setup like this.

Edit: There’s also a command utility to compile, but I haven’t used it.
https://developer-old.gnome.org/gtkmm-tutorial/stable/sec-gio-resource.html.en

This cannot be done realistically, the builder supports custom tags that need to be parsed at runtime. See GtkBuildable for details of how this is implemented. The only way to handle custom tags would be to hard code every widget in the Rust parser, which is not a good idea.

There are some existing macros that can get a bit closer to it:

  • Use #[derive(gtk4::CompositeTemplate)], but note this still has to parse the XML at runtime

  • Use relm4 and relm4::view!, but note this is just a nicer syntax for writing the declarations in Rust, and it can require you to hard code in trait implementations manually

One of the benefits of using external XML files is also how you can change the GUI around without recompiling the whole app, that can really help with development speed.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.