GTK4 and pango markup in menu items

to build a clean GTK4 version of my program (https://atomes.ipcms.fr) I need to be able to add pango markup in GTK4 menus, and, the menu(s) must be C coded and not read from XML file(s).

This question follows the one from this post (that contains a visual illustration):

A little bit more than a year ago I asked the same thing, got answers, waited for a long time for updates in the GTK4 lib, hopping for the answer to work at some point … and no … or maybe it does and I am doing something wrong …

Basically it works when reading the menu from a XML file, and it does not when coding directly the menu elements, and I really, really, must do it this way.

Here is a piece of example code I wrote:

#include <gtk/gtk.h>

GtkApplication * TestApp = NULL;

void menu_bar_action (GSimpleAction * action, GVariant * parameter, gpointer data)
{
  gchar * name = g_strdup_printf ("%s", g_action_get_name(G_ACTION(action)));
  if (g_strcmp0 (name, "quit") == 0)
  {
    g_application_quit (G_APPLICATION(TestApp));
  }
}

GMenuItem * create_gmenu_item (const gchar * label, const gchar * action)
{
  GMenuItem * item;
  item = g_menu_item_new (label, action);
  // Requesting to use markup here:
  g_menu_item_set_attribute (item, "use-markup", "b", TRUE, NULL);
  return item;
}

void append_menu_item (GMenu * menu, const gchar * label, const gchar * action)
{
  GMenuItem * item = create_gmenu_item (label, action);
  g_menu_append_item (menu, item);
  g_object_unref (item);
}

void run_program (GApplication * app, gpointer data)
{
  GtkWidget * window = gtk_application_window_new (GTK_APPLICATION(app));
  gtk_window_set_title (GTK_WINDOW(window), "Test");
  gtk_window_set_resizable (GTK_WINDOW(window), TRUE);
  gtk_widget_set_size_request (window, 900, 450);

#ifdef UIMODE
  GtkBuilder * builder = gtk_builder_new ();
  gtk_builder_add_from_file (builder, "test.ui", NULL);
  gtk_application_set_menubar (GTK_APPLICATION (app),
                               G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")));
  g_object_unref (builder);
#else
  GMenu * menubar = g_menu_new();
  GMenu * menu    = g_menu_new ();
  // Trying different things just in case:
  append_menu_item (menu, "C(H)<sub>2</sub>", "None");
  append_menu_item (menu, "C(H)&lt;sub&gt;3&lt;/sub&gt;", "None");
  append_menu_item (menu, "C(H)<sub>4</sub>", "None");
  append_menu_item (menu, "Quit", "app.quit");
    
  g_menu_append_submenu (menubar, "File", G_MENU_MODEL (menu));
  g_object_unref (menu);
    
  gtk_application_set_menubar (GTK_APPLICATION(app), G_MENU_MODEL(menubar));
#endif  
  GSimpleAction * act = g_simple_action_new ("quit", NULL);
  g_signal_connect (act, "activate", G_CALLBACK(menu_bar_action), NULL);
  g_action_map_add_action (G_ACTION_MAP(GTK_APPLICATION(app)), G_ACTION(act)); 
  gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW(window), TRUE);
  gtk_window_present (GTK_WINDOW(window)); 
}

int main (int argc, char *argv[])
{
  // setlocale(LC_ALL,"en_US");
  gtk_disable_setlocale ();
  TestApp = gtk_application_new (g_strdup_printf ("test._%d.gtk", (int)clock()), G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (G_OBJECT(TestApp), "activate", G_CALLBACK(run_program), NULL);
  int status = g_application_run (G_APPLICATION (TestApp), 0, NULL);
  g_object_unref (TestApp);
  return status;
}

And the ‘test.ui’ optional XML file for the menu:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <menu id="menubar">
    <submenu>
      <attribute name="label" translatable="yes">Test</attribute>
      <section>
        <item>
	  <attribute name="label" translatable="yes">C(H)&lt;sub&gt;2&lt;/sub&gt;</attribute>
          <attribute name="use-markup">TRUE</attribute>
          <attribute name="action">None</attribute>
        </item>
        <item>
	  <attribute name="label" translatable="yes">C(H)&lt;sub&gt;3&lt;/sub&gt;</attribute>
          <attribute name="use-markup">TRUE</attribute>
          <attribute name="action">None</attribute>
        </item>
        <item>
	  <attribute name="label" translatable="yes">C(H)&lt;sub&gt;3&lt;/sub&gt;</attribute>
          <attribute name="use-markup">TRUE</attribute>
          <attribute name="action">None</attribute>
        </item>
        <item>
	  <attribute name="label" translatable="yes">Quit</attribute>
          <attribute name="action">app.quit</attribute>
        </item>
      </section>
    </submenu>
  </menu>
</interface>

If I build the code using:

#!/bin/bash

app=$1

LIBS="-Wl,--export-dynamic `pkg-config --libs gtk4`"
INCLUDES="`pkg-config --cflags gtk4`"
CFLAGS="-O2"
gcc -o $app'.x' $CFLAGS $INCLUDES $app'.c' $LIBS -DUIMODE

Then the menu, loaded from the XML file properly uses pango markup.

However If I build the code using:

#!/bin/bash

app=$1

LIBS="-Wl,--export-dynamic `pkg-config --libs gtk4`"
INCLUDES="`pkg-config --cflags gtk4`"
CFLAGS="-O2"
gcc -o $app'.x' $CFLAGS $INCLUDES $app'.c' $LIBS

Then no markup.

I tested this on a clean, all new Debian 12, with GTK 4.8.3.

You will understand based on my examples that I want to display chemical formula.

Thanks in advance if you have any ideas to help !

Best regards :wink:

So to summarize, it works with XML, but not with code (by calling functions).

My suggestion is then to generate XML on the fly, and load that XML with code.

(There is g_markup_escape_text() and related functions to help generate the XML text).

That hardly seems feasible or advisable… at least if we can’t prove this isn’t a bug in GTK, which it sure looks like, since I would expect attributes to be honoured regardless of how they were initially set.

Could the issue be the GVariant format string here? I would expect it to be boolean, not &s which seems to mean pointer-to-string… so yes, I suspect it’s a bug. I’ll try to test and submit a merge request to fix that.

image

1 Like

In the meantime, does the code work if changed to

  g_menu_item_set_attribute (item, "use-markup", "s", "TRUE", NULL);

?

This seems to work for me; I updated tests/testmenubutton.c to put the items in <i>italics</i> and tested with both versions of set_attribute(), and the one using "s", "TRUE" does work, while the "b", TRUE does not.

From a glance at the gtkbuilder-menus.c code, I think Builder assumes all attributes are strings, unless they have a XML type attribute saying otherwise:

image

and I suppose it might be a bit late in the game to change the attribute to explicitly be a boolean everywhere, or may break someone else’s code…

Meaning that our options then becomes to make the in-code menu building also expect a string, not a boolean.

Glancing through the codebase, I think that should work fine for app menus, and testmenubutton, but it looks like other menus have varying to zero support for the use-markup attribute, e.g. tests/testgmenu does not respect it when asked to <b>bold</b>. I guess support for those would have to be added on a case-by-case basis.

Hello @dboles ,
and thank you for taking some time to help/investigate :wink:

I am sorry to tell you that on my side the "s", TRUE argument does not work,
… because I forgot the double quotes "TRUE"

Thank you now it works perfectly !

1 Like

Glad to help, wouldn’t want to see you having to make a 4th thread or whatever :grinning:

1 Like

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