How do I add custom shortcuts to functions in gtk4

Hello, I am having trouble adding custom keyboard shortcuts to gtk4 application. I just need to call a simple function like.

void printmsg(void) {
printf(“hello, world\n”);
}

Documentation goes over accelerators but it only shows how to set shortcuts for “actions” like “window.close” and I couldn’t figure out how to make custom actions for existing classes either.

Thank you!

You can add new actions by running Gtk.WidgetClass.install_action:

However, this only works when initially setting up the class, and therefore only for a subclass.

Is there a good reason you can’t use a subclass of Gtk.Widget?

Hi,

I usually use actions for that, but there seems to be another way:

Hello, thank you for the instructions and links, I really appreciate it.
I couldn’t get it to work tho, here is what I put together for now.

	/* window */
	window = gtk_application_window_new(app);
	gtk_window_set_title(GTK_WINDOW(window), "MMenu");
	gtk_window_set_default_size(GTK_WINDOW(window), 600, 250);

	/* shortcut */
	GtkEventController *controller = gtk_shortcut_controller_new();
	gtk_widget_add_controller(window, controller);
	GtkShortcutAction *action = gtk_callback_action_new(printmsg, NULL, NULL);
	GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string("<Alt>c");
	GtkShortcut *shortcut = gtk_shortcut_new(trigger, action);
	gtk_shortcut_controller_add_shortcut(GTK_SHORTCUT_CONTROLLER(controller), shortcut);

printmsg:

gboolean printmsg(GtkWidget *widget, GVariant *args, gpointer user_data) {
	printf("hello, world");
    return true;
}

I messed around with gtk_keyval_trigger_new but it was the same result.

Could you please explain how you use actions? That could work just well.

First of all, you want your window to be a subclass of Gtk.ApplicationWindow. This allows you to extend it, for example with new actions.

Then, when the class is initialized, you run Gtk.WidgetClass.install_action to add the action to the subclass.

Using C, this should look similar to this:

// The class structure
struct _ExampleAppWindow
{
  GtkApplicationWindow parent;
};

// Definition of subclass
G_DEFINE_TYPE(ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW);

// Instance initialization (run for every instance)
static void
example_app_window_init (ExampleAppWindow *app)
{
}

// Class initialization (run onces for the class)
static void
example_app_window_class_init (ExampleAppWindowClass *class)
{
  // Install actions here like this
  gtk_widget_class_install_action(*class, "window.printmsg", NULL, example_app_window_printmsg)
}

// Constructor for window subclass
ExampleAppWindow *
example_app_window_new (ExampleApp *app)
{
  return g_object_new (EXAMPLE_APP_WINDOW_TYPE, "application", app, NULL);
}

// Your callback function for the action
void
example_app_window_printmsg (Gtk.Widget *widget, const char *action_name, GVariant *parameter)
{
  // Run your action code here
}

You can install actions to any custom widget in this way, it just needs to be a subclass you’ve defined.

1 Like

Note that subclassing is not strictly necessary to add actions.
It’s also possible to insert a Gio.SimpleActionGroup() to any widget, and add custom actions to it.

I gave a few examples in old topics:

1 Like

Strange, your code looks fine…
I tried this in Python, and it works:

c = Gtk.ShortcutController()
a = Gtk.CallbackAction.new(lambda *_a: print(_a) or True, None, None)
t = Gtk.ShortcutTrigger.parse_string("<Control>l")
s = Gtk.Shortcut.new(t, a)
c.add_shortcut(s)
window.add_controller(c)

Also, be careful, some <Alt> shortcuts are captured by desktop environments, so won’t reach your app.

Like gwillems first said, I usually just use actions for hotkeys. But using shortcuts seems to work fine too.

import gi
from gi.repository import Gio
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class Main:
  def __init__(self, app):
    self.window = Gtk.ApplicationWindow.new(app)
    
    _action = Gio.SimpleAction.new("action0", None)
    _action.connect("activate", self.action_callback)
    app.add_action(_action)
    app.set_accels_for_action("app.action0", ["<Ctrl>h"])
    
    c = Gtk.ShortcutController()
    a = Gtk.CallbackAction.new(self.action_callback, None, None)
    t = Gtk.ShortcutTrigger.parse_string("<Control>l")
    s = Gtk.Shortcut.new(t, a)
    c.add_shortcut(s)
    self.window.add_controller(c)
    
    self.window.present()

  def action_callback(self, _1, _2, _3=None, _4=None):
    print("hello world")

app = Gtk.Application()
app.connect('activate', Main)
app.run(None)

Thank you, I will definitely look into this.

1 Like

I will have to read about subclasses to try this but it looked like something too advanced for a simple process so I didn’t look much into this. Still, if it can solve the problem I have zero complaints.

Thank you very much for the example, it works perfectly. I will still check other suggested methods to see if there is any simpler ways but this definetely solves my current problem.

Here is another example of how I worked it out for myself:



/*
 * Mo 11. Nov 11:38:44 CET 2024 
 */

#include"shortcout-callback.h"

gboolean callback( GtkWidget* widget,  GVariant* args,  gpointer user_data)
{
	g_print("Callback\n");
}

void destroy(gpointer data)
{
	g_print("destroy\n");
}

void activate (GtkApplication *app, gpointer data)
{
  GtkWidget *window;
  GtkEventController *shortcut_controller;
  GtkShortcut *shortcut;
  GtkShortcutTrigger *trigger;
  GtkShortcutAction *action;

  window =gtk_application_window_new(app);
  gtk_widget_set_size_request(window,50,50);
   
  // Create a GtkShortCutTrigger 
  trigger = gtk_shortcut_trigger_parse_string ("<Control><Alt>S");
  
  // create a GtkShortCutCallback
  action = gtk_callback_action_new (callback, data,  destroy);

  //Create a ShortCut - Connect action to the trigger
  shortcut =gtk_shortcut_new (trigger, action);
 
  // Creating a Shortcut Controller
  shortcut_controller = gtk_shortcut_controller_new();

  // Area for which the controller works
  // gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER(shortcut_controller),GTK_SHORTCUT_SCOPE_MANAGED);
  // gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER(shortcut_controller),GTK_SHORTCUT_SCOPE_LOCAL);
  gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER(shortcut_controller),GTK_SHORTCUT_SCOPE_GLOBAL);

  //Add a shortcut
  gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER(shortcut_controller),shortcut);
  
   // Setting the ShortCutController on the widget
   gtk_widget_add_controller (window,shortcut_controller);

   gtk_widget_set_visible(window,TRUE);
}

Have fun testing and programming

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