gtk3: Как распространять ключевые события / ускорители (ярлыки) с локальных на глобальные? - PullRequest
0 голосов
/ 23 апреля 2020

Настройка

Это (пример) GUI ситуация:

  • Окно
    • GtkEntry
    • Меню
      • MenuItem (ускоритель: backspace )

Я использую gtk_window_add_accel_group и gtk_widget_add_accelerator для настройки ускорителя.

Проблема

Когда я нажимаю Backspace в пределах GtkEntry, акселератор активируется.

Goal

Goal Для этого примера

Если в GtkEntry есть удар на обратную клавишу, он должен действовать как обычно и удалять персонажа. Если любой другой виджет сфокусирован, ускоритель должен активироваться.

Цель в целом

Цель состоит в том, чтобы иметь поведение, при котором локальные виджеты имеют более высокий приоритет относительно ввода, чем глобальные виджеты. Местные означает "сосредоточены" или их родители. Например,

  • Окно
    • Виджет1
      • Виджет2
        • Виджет3
        • GtkEntry (в фокусе)

Цепочка распространения для ввода должна быть:

GtkEntry, Widget3, Widget2, Widget1, Window.

Относительно ответов

  • Я знаю, что вы можете отключить указанную группу c ускорителей. Но это не приемлемое решение. Общая причина - чистота кода. Конкретные причины приведены ниже.
    • GtkEntry должен знать о функциональности, которая не входит в его компетенцию.
    • Если имеется много групп ускорителей, все они должны быть отключены и повторно подключены.
    • Если ускорители определяются пользователем, даже неясно, какие группы ускорителей необходимо отключить.
  • Если это невозможно с помощью gtk, я также буду признателен за эту информацию.

Код

Ниже приведен полный код .

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

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

  // ======= setup window generalls =====
  GtkWindow *window = (GtkWindow*)gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

  // ======= create GtkEntry =======
  gtk_box_pack_start(GTK_BOX(vbox), (GtkWidget*)gtk_entry_new(), FALSE, FALSE, 0);

  // ======= create menu bar =======
  {
    GtkMenu *menubar = (GtkMenu*)gtk_menu_bar_new();
    gtk_box_pack_start(GTK_BOX(vbox), (GtkWidget*)menubar, FALSE, FALSE, 0);

    // create a new menu item in menu bar
    GtkMenuItem *menuTitle = (GtkMenuItem *)gtk_menu_item_new_with_label("MenuBarItem");
    gtk_menu_shell_append(GTK_MENU_SHELL(menubar), (GtkWidget*)menuTitle);
    {
      // create a new dropdown menu
      GtkMenu *menu = (GtkMenu *)gtk_menu_new();
      gtk_menu_item_set_submenu(menuTitle,(GtkWidget*)menu);
      {
        GtkWidget *menuItem4 = gtk_menu_item_new_with_label("Quit");
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem4);
        g_signal_connect(menuItem4, "activate", G_CALLBACK(gtk_main_quit), NULL);

        GtkAccelGroup *accelGroup = gtk_accel_group_new();
        gtk_window_add_accel_group(GTK_WINDOW(window), accelGroup);
        gtk_widget_add_accelerator(menuItem4, "activate", accelGroup, 
                  GDK_KEY_BackSpace , (GdkModifierType)0, GTK_ACCEL_VISIBLE);
      }
    }    
  }

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

  return 0;
}

1 Ответ

0 голосов
/ 04 мая 2020

Просто подключитесь к key-press-event и key-release-event окна:

g_signal_connect(window,"key-press-event",G_CALLBACK(gtk_window_propagate_key_event),NULL);
g_signal_connect(window,"key-release-event",G_CALLBACK(gtk_window_propagate_key_event),NULL);

gtk_window_propagate_key_event сделает всю работу за вас (в том числе проверку на предмет фокусированного виджета)

Замечания

  • Этот метод также работает в сочетании с gtk_application_set_accels_for_action (поскольку в окне, по-видимому, зарегистрированы ускорители приложений)
  • Цитата из справки gtk: "борьба с GTK закончена" ключевые события вряд ли зажгут счастье "

Этот метод в настоящее время используется в

  • Наутилус (гном)
    • (конкретная реализация отличается в зависимости от времени существо)
...