GtkMenuButton Menu Model causes crashes in Windows-compiled app

I’ve been writing an application in Rust using the gtk4-rs bindings for GTK. Recently, I’ve tried compiling my application on Windows under the UCRT64 builds of Rust and GTK4, but the application will not launch (seemingly) because of the MenuModel attached to a GtkMenuButton. Here is a small MWE that (at least on my two computers) shows the difference in functionality (note - MWE is in both C and Rust since it was originally written in Rust, then I ported that example to C at the behest of the gtk4-rs folks to make sure it was the GTK library, and not the Rust crate):

test.ui

<?xml version='1.0' encoding='UTF-8'?>
<interface>
  <requires lib="gio" version="2.0"/>
  <requires lib="gtk" version="4.12"/>
  <object class="GtkMenuButton" id="button">
    <property name="menu-model">
      <menu id="test-menu">
        <item>
          <attribute name="action">window.close</attribute>
          <attribute name="label">Close Window</attribute>
        </item>
      </menu>
    </property>
  </object>
</interface>

main.c

#include <gtk/gtk.h>

static void
activate (GtkApplication *app,
          gpointer        user_data)
{
    GtkWidget *window;
    GtkWidget *button;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Hello");
    gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

    // Builder
    GtkBuilder *builder = gtk_builder_new ();
    gtk_builder_add_from_file (builder, "test.ui", NULL);

    GObject *menubutton = gtk_builder_get_object (builder, "button");
    gtk_window_set_child (GTK_WINDOW (window), (GtkWidget*)menubutton);

    gtk_window_present (GTK_WINDOW (window));
}

int
main (int    argc,
      char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

Compiled with gcc $(pkg-config --cflags gtk4) -o test main.c $(pkg-config --libs gtk4)

main.rs

use gtk4::glib;
use gtk4::prelude::*;

fn main() -> glib::ExitCode {
    let app = gtk4::Application::builder()
        .application_id("test.test.test")
        .build();

    app.connect_activate(|app| {

        let window = gtk4::ApplicationWindow::builder()
            .application(app)
            .build();

        let menubut = gtk4::Builder::from_string(include_str!("test.ui"));
        let menubut: gtk4::MenuButton = menubut.object("button").unwrap();

        window.set_child(Some(&menubut));

        window.present();
    });

    app.run()
}

Observed Behavior

The behavior of these two examples is slightly different, so they will both be described:

C Minimal Example

  • On Arch Linux, the example works as expected, allowing the user to click on the button and displaying the menu.
  • On Windows, the example loads, but the button is greyed out and unable to be interacted with.

Rust Minimal Example

  • As before, on Arch Linux, the example works as expected.
  • On Windows, the example crashes on startup, with the error Gtk-ERROR **: [...]: failed to add UI: .:6.34 Object with ID not found (the spacing between “ID” and “not” is accurate and intentional).

Software Versions

  • Windows
    • Rust 1.84.1
    • gtk4-rs 0.9.6
    • GTK 4.16.12 (Installed via UCRT64 in MSYS2)
  • Linux
    • Rust 1.86.0
    • gtk4-rs 0.9.6
    • GTK 4.18.3 (Up-to-date Arch Linux)

I’d really appreciate any help possible to figure out how to get this to work (especially if it ends up being user error!) If I can provide anything else to help find the problem, please let me know, and thank you in advance!

Your Windows environment uses Gtk 4.16 but support for inline menu definitions like in your UI example was not added before Gtk 4.17. See Gtk news:

Do read these news files; they provide valuable information.

And do handle errors when loading UI files. Your C example doesn’t, and hence fails silently. Had you passed an error pointer to the gtk_builder_add_from_file call you might have seen a similar error as in your Rust code.

Your Rust code crashes because you’re calling gtk::Builder::from_string, i.e. gtk_builder_new_from_string which explicitly crashes when given an invalid UI definition.

For backwards compatibility define the menu separately, and then reference it by name in the menu-model property definition. Roughly like this (untested):

<?xml version='1.0' encoding='UTF-8'?>
<interface>
  <requires lib="gio" version="2.0"/>
  <requires lib="gtk" version="4.12"/>
  <object class="GtkMenuButton" id="button">
    <property name="menu-model">test-menu</property>
  </object>
  <menu id="test-menu">
    <item>
      <attribute name="action">window.close</attribute>
      <attribute name="label">Close Window</attribute>
    </item>
  </menu>
</interface>
2 Likes

Thank you! Trying to use a feature that doesn’t exist yet would be a perfectly reasonable explanation for stuff not working :sweat_smile:

As for your suggestions, implementing the menus separately worked like a charm, and is roughly how you put it in your untested example. As for error handling, I agree with you that the error handling in the MWEs was lacking - in the C code, that’s primarily because I’m not as familiar with the API and was just looking to roughly translate the Rust code to C. In the actual application I’m developing using the Rust bindings, I primarily use Composite Templates, which have some inbuilt error-checking at app startup. The original error message I was getting with the app in Windows mentions something about the menu object requiring the id property, hence how I knew to start debugging at the menu. That error wasn’t included in the original post because the MWE code intentionally gets past that error by providing the id property in the menu object. All of that is to say that error handling is something that is being kept in mind, but I appreciate the suggestion, as well as the explanations and relevant documentation!