GTK3 - shortcuts window using C code, not XML file

Hello,
trying to code a shortcuts window, so not using a XML file, the following code works for GTK4 (before and after GTK4.14.4), it compiles for GTK3, but in that case the shortcuts window appears empty (it displays the title of the window, the search button, but not shortcut sections, group, or buttons), any idea why ?

void show_the_widgets (GtkWidget * widg)
{
#ifdef GTK4
  gtk_widget_set_visible (widg, TRUE);
#else
  gtk_widget_show_all (widg);
#endif
}

void window_add_child (GtkWidget * win, GtkWidget * child)
{
#ifdef GTK3
  gtk_container_add (GTK_CONTAINER(win), child);
#else
  gtk_window_set_child ((GtkWindow *)win, child);
#endif
}

void add_box_child (GtkWidget * box, GtkWidget * child)
{
#ifdef GTK4
  gtk_box_append (GTK_BOX(box), child);
#else
  gtk_box_pack_start (GTK_BOX(box), child, FALSE, FALSE, 0);
#endif
}

shortcuts w_shortcuts[] = {
  { "About", "open about dialog", GDK_KEY_a, "<Ctrl>a" },
  { "Periodic table", "open periodic table", GDK_KEY_p, "<Ctrl>p" },
  { "Quit", "quit atomes", GDK_KEY_q, "<Ctrl>q" }
};

void shortcuts_window ()
{
  GtkShortcutsWindow * win = g_object_new (GTK_TYPE_SHORTCUTS_WINDOW, "title", "Shortcuts", "section-name", "smain" , "modal", FALSE, "resizable", FALSE, NULL);
  GtkShortcutsSection * short_cut_section = g_object_new (GTK_TYPE_SHORTCUTS_SECTION, "visible", TRUE, "view-name", "main", "section-name", "smain", NULL);
  GtkShortcutsGroup * short_cut_group = g_object_new (GTK_TYPE_SHORTCUTS_GROUP, "visible", TRUE, "title", "Group", "view", "main", NULL);
  GtkShortcutsShortcut * shortcut;
  int i;
  for (i=0; i<G_N_ELEMENTS(shortcuts); i++)
  {
    shortcut = g_object_new (GTK_TYPE_SHORTCUTS_SHORTCUT,
                             "visible",       TRUE,
                             "icon-set",      TRUE,
                             "shortcut-type", GTK_SHORTCUT_ACCELERATOR,
                             "accelerator",   w_shortcuts[i].accelerator,
                             "title",         w_shortcuts[i].description,
                             "subtitle",      w_shortcuts[i].subtitle,
                             "subtitle-set",  FALSE,
                             "direction",     GTK_TEXT_DIR_LTR,
                             NULL );
#ifdef GTK4
#if GTK_MINOR_VERSION > 14 || (GTK_MINOR_VERSION == 14 && GTK_MICRO_VERSION >= 4)
    gtk_shortcuts_group_add_shortcut (short_cut_group, shortcut);
#else
    add_box_child ((GtkWidget *)short_cut_group, (GtkWidget *)shortcut);
#endif
#else
    add_box_child ((GtkWidget *)short_cut_group, (GtkWidget *)shortcut);
#endif
  }
#ifdef GTK4
#if GTK_MINOR_VERSION > 14 || (GTK_MINOR_VERSION == 14 && GTK_MICRO_VERSION >= 4)
  gtk_shortcuts_section_add_group (short_cut_section, short_cut_group);
  gtk_shortcuts_window_add_section (win, short_cut_section);
#else
  add_box_child ((GtkWidget *)short_cut_section, (GtkWidget *)short_cut_group);
  window_child ((GtkWidget *)win, (GtkWidget *)short_cut_section);
#endif
#else
  add_box_child ((GtkWidget *)short_cut_section, (GtkWidget *)short_cut_group);
  window_child ((GtkWidget *)win, (GtkWidget *)short_cut_section);
#endif
  show_the_widgets ((GtkWidget *)win);
}

Hi,

Can you try to add this?

show_the_widgets ((GtkWidget *)short_cut_section);

(also, beware, you loop over shortcuts[] instead of main_shortcuts[]…)

I corrected the code, I let few glitches in including what you spotted,
because I adapted it from the one I have been working on.

As for the show_the_widgets ((GtkWidget *)short_cut_section); it has no effects.

Hard to spot the issue without the full code…

By the way, you manually set a lot of properties… On my side I only set a few of them:

  • section-name for the GtkShortcutsWindow
  • section-name for the GtkShortcutsSection
  • title and action-name for the GtkShortcutsShortcut

The other properties should be resolved automatically.

Please find attached a working example code:

Example code
#include <gtk/gtk.h>

GtkApplication * TestApp = NULL;

void show_the_widgets (GtkWidget * widg)
{
#ifdef GTK4
  gtk_widget_set_visible (widg, TRUE);
#else
  gtk_widget_show_all (widg);
#endif
}

void window_add_child (GtkWidget * win, GtkWidget * child)
{
#ifdef GTK3
  gtk_container_add (GTK_CONTAINER(win), child);
#else
  gtk_window_set_child ((GtkWindow *)win, child);
#endif
}

void add_box_child (GtkWidget * box, GtkWidget * child)
{
#ifdef GTK4
  gtk_box_append (GTK_BOX(box), child);
#else
  gtk_box_pack_start (GTK_BOX(box), child, FALSE, FALSE, 0);
#endif
}

typedef struct shortcuts shortcuts;
struct shortcuts
{
  gchar * description;                                       /*!< Shortcut description */
  gchar * subtitle;                                          /*!< Shortcut subtitle */
  gint key;                                                  /*!< Shortcut key */
  gchar * accelerator;                                       /*!< Shortcut accelerator */
};

shortcuts w_shortcuts[] = {
  { "About", "open about dialog", GDK_KEY_a, "<Ctrl>a" },
  { "Periodic table", "open periodic table", GDK_KEY_p, "<Ctrl>p" },
  { "Quit", "quit atomes", GDK_KEY_q, "<Ctrl>q" }
};

void shortcuts_window ()
{
  GtkShortcutsWindow * win = g_object_new (GTK_TYPE_SHORTCUTS_WINDOW, "section-name", "smain" , "modal", FALSE, "resizable", FALSE, NULL);
  GtkShortcutsSection * short_cut_section = g_object_new (GTK_TYPE_SHORTCUTS_SECTION, "visible", TRUE, "section-name", "smain", NULL);
  GtkShortcutsGroup * short_cut_group = g_object_new (GTK_TYPE_SHORTCUTS_GROUP, "visible", TRUE, "title", "Group", NULL);
  GtkShortcutsShortcut * shortcut;
  int i;
  for (i=0; i<G_N_ELEMENTS(w_shortcuts); i++)
  {
    shortcut = g_object_new (GTK_TYPE_SHORTCUTS_SHORTCUT,
                             "visible",       TRUE,
                             "shortcut-type", GTK_SHORTCUT_ACCELERATOR,
                             "accelerator",   w_shortcuts[i].accelerator,
                             "title",         w_shortcuts[i].description,
                             NULL );
#ifdef GTK4
#if GTK_MINOR_VERSION > 14 || (GTK_MINOR_VERSION == 14 && GTK_MICRO_VERSION >= 4)
    gtk_shortcuts_group_add_shortcut (short_cut_group, shortcut);
#else
    add_box_child ((GtkWidget *)short_cut_group, (GtkWidget *)shortcut);
#endif
#else
    add_box_child ((GtkWidget *)short_cut_group, (GtkWidget *)shortcut);
#endif
  }
#ifdef GTK4
#if GTK_MINOR_VERSION > 14 || (GTK_MINOR_VERSION == 14 && GTK_MICRO_VERSION >= 4)
  gtk_shortcuts_section_add_group (short_cut_section, short_cut_group);
  gtk_shortcuts_window_add_section (win, short_cut_section);
#else
  add_box_child ((GtkWidget *)short_cut_section, (GtkWidget *)short_cut_group);
  window_child ((GtkWidget *)win, (GtkWidget *)short_cut_section);
#endif
#else
  add_box_child ((GtkWidget *)short_cut_section, (GtkWidget *)short_cut_group);
  window_add_child ((GtkWidget *)win, (GtkWidget *)short_cut_section);
#endif
  show_the_widgets ((GtkWidget *)win);
}

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));
  }
  else if (g_strcmp0 (name, "shortcuts") == 0)
  {
   shortcuts_window ();
  }
}

GMenuItem * create_gmenu_item (const gchar * label, const gchar * action)
{
  GMenuItem * item;
  item = g_menu_item_new (label, action);
  g_menu_item_set_attribute (item, "use-markup", "s", "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, 350, 350);

  GMenu * menubar = g_menu_new ();
  GMenu * menu    = g_menu_new ();
  append_menu_item (menu, "Shortcuts", "app.shortcuts");
  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));
  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)); 
  act = g_simple_action_new ("shortcuts", 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);
#ifdef GTK3
  gtk_widget_show (window);
#else
  gtk_window_present (GTK_WINDOW(window)); 
#endif
}

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;
}

It can be compiled using this script:

#!/bin/bash

# no '.c' ext
app=$1
gtk=$2

if [ $gtk -eq "3" ]; then  
  LIBS="-Wl,--export-dynamic `pkg-config --libs gtk+-3.0`"
  INCLUDES="`pkg-config --cflags gtk+-3.0`"
  FLAGS="-DGTK3"
else
  LIBS="-Wl,--export-dynamic `pkg-config --libs gtk4`"
  INCLUDES="`pkg-config --cflags gtk4`"
  FLAGS="-DGTK4"
fi
CFLAGS=$FLAGS" -O0 -Wall -g3 -pg -ggdb3 -cpp -dA -dD -dH -dp -dP -fvar-tracking -fbounds-check -fstack-protector-all -Wduplicated-cond"
# CFLAGS="-O2"
gcc -o $app'.x' $CFLAGS $app'.c' $LIBS $INCLUDES

Note in GTK4 case it works, not in the GTK3 case, I tried to simply the instructions to follow your advise, so far no success.

Thanks in advance for your lights on the matter.

It appears you need to use gtk_container_add() for GTK3 because the widgets uses that interface for collecting their children. Verified this using the source code: GtkShortcutsWindow, GtkShortcutsGroup, GtkShortcutsSection.

1 Like

Nailed it thank you !

I also prefer to write code for creating a GtkShortcutsWindow. libgedit-amtk has some helper functions for that, and a small test program demonstrating how it works.

1 Like