Search code examples
cgtk3

Implementing overlapping shortcuts


I'm trying to implement the Ctrl+C shortcut a widget without it disturbing other defined shortcuts.

Problem

My window looks like this:

  • GtkWindow
    • GtkEntry
    • GtkToggleButton

Part of the code

// --- add checkbox ---
GtkWidget * checkbutton = gtk_check_button_new_with_label("My Checkbox");
gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(checkbutton));

// --- setup checkbox shortcut ---

GtkAccelGroup * accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);   

gtk_widget_add_accelerator(checkbutton, "clicked", accel_group, 
                          GDK_KEY_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 
g_signal_connect(checkbutton, "clicked", G_CALLBACK(onCopyCheckbox), NULL);    
  // problem: this event fires, even if GtkEntry is focussed
  //          it then block the <kbd>Ctrl+C</kbd>-Event of GtkEntry

Expected behaviour

  • If GtkEntry is focussed and Ctrl+C is pressed, the function callback1() should be triggered.
  • If GtkToggleButton is focussed and Ctrl+C is pressed, it should print "onCopyCheckbox() called\n".

Actual behavior

  • If GtkEntry is focussed and Ctrl+C is pressed, "onCopyCheckbox() called\n" gets printed and nothing gets copied.
  • If GtkToggleButton is focussed and Ctrl+C is pressed, "onCopyCheckbox() called\n" gets printed.

Please do not...

  • tell me I should use a different shortcut / accelerator.

Full compilable and executable code:

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

// ------------------------------------------------------------

void
onCopyCheckbox (GtkWidget      *widget,
                GdkDragContext *context,
                gpointer        user_data)
{
  printf("onCopyCheckbox() called\n");
}

// ------------------------------------------------------------

void fillWindow (GtkWindow * window)
{

  // ------- create layout ------------
  GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  gtk_container_add(GTK_CONTAINER(window),GTK_WIDGET(vbox)); 

  // --- add line edit --- 
  GtkWidget * lineedit = gtk_entry_new();
  gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(lineedit));

  // --- add checkbox ---
  GtkWidget * checkbutton = gtk_check_button_new_with_label("My Checkbox");
  gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(checkbutton));

  // --- setup checkbox shortcut ---

  GtkAccelGroup * accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);   

  gtk_widget_add_accelerator(checkbutton, "clicked", accel_group, 
                            GDK_KEY_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 
  g_signal_connect(checkbutton, "clicked", G_CALLBACK(onCopyCheckbox), NULL);    
    // problem: this event fires, even if GtkEntry is focussed
    //          it then block the <kbd>Ctrl+C</kbd>-Event of GtkEntry

}

// ------------------------------------------------------------

int main(int argc, char *argv[]) {

  gtk_init(&argc, &argv);

  GtkWindow * window;
  {
    window = (GtkWindow*)gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (window, "Window title");
    gtk_window_set_default_size (window, 200, 200);
    g_signal_connect(window, "destroy", gtk_main_quit, NULL);
  }

  fillWindow(window);

  gtk_widget_show_all ((GtkWidget*)window);
  gtk_main();

  return 0;
}

// ------------------------------------------------------------

Solution

  • Imagine you want to spawn the function below on Ctrl+C while the checkbox is focussed.

    void 
    shortcutAction(GSimpleAction* a, GVariant * b, gpointer c){
      printf("shortcutAction() called\n");
    }
    

    This is how you setup your checkbox

    // --- add checkbox ---
    GtkWidget * checkbox = gtk_check_button_new_with_label("My Checkbox");
    gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(checkbox));
    
    // ========= setup checkbox shortcut ==============
    
    GtkApplication * app = GTK_APPLICATION(g_application_get_default());
    
    // ------- Part 1: create the action itself -----------
      // All the action you want to add go in one ActionGroup
      GSimpleActionGroup * group = g_simple_action_group_new();
    
      // define all action you want to have
      //  these action can be basically seen as some kind of signal  
      static const GActionEntry actions[] {
        { .name="print", .activate=shortcutAction}
      };
    
      // ActionGroup.add(actions[])        
      g_action_map_add_action_entries(G_ACTION_MAP(group), actions, G_N_ELEMENTS(actions), NULL);
    
      // Widget.addActionGroup(ActionGroup) // connect widget and action group
      //  No you also create a name for the group. Here "checkbox" (but can be anything)
      gtk_widget_insert_action_group (GTK_WIDGET(checkbox), "checkbox", G_ACTION_GROUP(group));
    
    // ------- Part 2: create the shortcut to create the action --------
    const gchar * const shortcuts[] = { "<Ctrl>C", NULL };
    gtk_application_set_accels_for_action (app, "checkbox.print", shortcuts);
    

    Remarks

    As you see in the code, there's a strict difference between

    • defining actions
    • defining shortcuts