diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-04-23 01:50:56 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-04-27 01:51:51 +0300 |
commit | 4c79d26a15385af046a18b05b7068e5878faf93c (patch) | |
tree | 3bfa41cbcce7837e6b01be9e28e076856d02da5c | |
parent | 5470fc3a63ff1cb15d7d5128e5c9d682dda28341 (diff) | |
download | sciteco-4c79d26a15385af046a18b05b7068e5878faf93c.tar.gz |
Gtk: fixed entering dead keys
* This is using an Input Method now.
* Entering dead keys has probably always been broken in Gtk which I only did not notice
because I use a keyboard layout without dead keys.
This affects the ^ and ` keys on a German layout.
* Once we support Unicode input, it would make sense to abuse Scintilla's already existing input method support.
Unfortunately, forwarding keyboard events to the Scintilla view breaks event freezing and results in flickering.
-rw-r--r-- | src/interface-gtk/interface.c | 88 |
1 files changed, 63 insertions, 25 deletions
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index 32f0011..8ccfd30 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -62,6 +62,8 @@ static void teco_interface_cmdline_size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data); +static void teco_interface_cmdline_commit_cb(GtkIMContext *context, gchar *str, + gpointer user_data); static gboolean teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data); static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, @@ -183,6 +185,7 @@ static struct { GtkWidget *message_widget; teco_view_t *cmdline_view; + GtkIMContext *input_method; GtkWidget *popup_widget; @@ -326,9 +329,12 @@ teco_interface_init(void) teco_view_ssm(teco_interface.cmdline_view, SCI_SETMARGINWIDTHN, 1, teco_view_ssm(teco_interface.cmdline_view, SCI_TEXTWIDTH, STYLE_ASTERISK, (sptr_t)"*")); teco_view_ssm(teco_interface.cmdline_view, SCI_MARGINSETTEXT, 0, (sptr_t)"*"); + /* only required as long as we avoid ordinary character representations */ teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETSTYLE, INDIC_CONTROLCHAR, INDIC_ROUNDBOX); teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETALPHA, INDIC_CONTROLCHAR, 128); teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETSTYLE, INDIC_RUBBEDOUT, INDIC_STRIKE); + /* we will forward key events, so the view should only react to text insertion */ + teco_view_ssm(teco_interface.cmdline_view, SCI_CLEARALLCMDKEYS, 0, 0); GtkWidget *cmdline_widget = teco_view_get_widget(teco_interface.cmdline_view); gtk_widget_set_name(cmdline_widget, "sciteco-cmdline"); @@ -338,6 +344,14 @@ teco_interface_init(void) gtk_container_add(GTK_CONTAINER(teco_interface.window), vbox); + teco_interface.input_method = gtk_im_context_simple_new(); + gtk_im_context_set_client_window(teco_interface.input_method, + gtk_widget_get_window(cmdline_widget)); + gtk_im_context_focus_in(teco_interface.input_method); + gtk_im_context_set_use_preedit(teco_interface.input_method, FALSE); + g_signal_connect(teco_interface.input_method, "commit", + G_CALLBACK(teco_interface_cmdline_commit_cb), NULL); + /* * Popup widget will be shown in the bottom * of the overlay widget (i.e. the Scintilla views), @@ -712,7 +726,8 @@ teco_interface_popup_clear(void) * event loop (e.g. using libX11 or Win32 APIs). * There already is a keyboard hook for Win32 in interface-curses. * On the downside, such solutions will probably freeze the window - * while SciTECO is busy. + * while SciTECO is busy. However we currently freeze the window + * anyway while being busy to avoid flickering. */ gboolean teco_interface_is_interrupted(void) @@ -799,12 +814,30 @@ teco_interface_set_css_variables(teco_view_t *view) gtk_widget_set_size_request(teco_view_get_widget(teco_interface.cmdline_view), -1, text_height); } +static void +teco_interface_cmdline_commit_cb(GtkIMContext *context, gchar *str, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + + /* + * FIXME: This is only for consistency as long as we + * do not support Unicode. + */ + for (char *p = str; *p != '\0'; p = g_utf8_next_char(p)) + if (g_utf8_get_char(p) >= 0x80) + return; + + if (!teco_cmdline_keypress(str, strlen(str), &error) && + g_error_matches(error, TECO_ERROR, TECO_ERROR_QUIT)) + gtk_main_quit(); +} + static gboolean -teco_interface_handle_key_press(guint keyval, guint state, GError **error) +teco_interface_handle_key_press(GdkEventKey *event, GError **error) { - teco_view_t *last_view = teco_interface_current_view; + const teco_view_t *last_view = teco_interface_current_view; - switch (keyval) { + switch (event->keyval) { case GDK_KEY_Escape: if (!teco_cmdline_keypress_c('\e', error)) return FALSE; @@ -832,7 +865,7 @@ teco_interface_handle_key_press(guint keyval, guint state, GError **error) break #define FNS(KEY, MACRO) \ case GDK_KEY_##KEY: \ - if (!teco_cmdline_fnmacro(state & GDK_SHIFT_MASK ? "S" #MACRO : #MACRO, error)) \ + if (!teco_cmdline_fnmacro(event->state & GDK_SHIFT_MASK ? "S" #MACRO : #MACRO, error)) \ return FALSE; \ break FN(Down, DOWN); FN(Up, UP); @@ -844,7 +877,7 @@ teco_interface_handle_key_press(guint keyval, guint state, GError **error) gchar macro_name[3+1]; g_snprintf(macro_name, sizeof(macro_name), - "F%d", keyval - GDK_KEY_F1 + 1); + "F%d", event->keyval - GDK_KEY_F1 + 1); if (!teco_cmdline_fnmacro(macro_name, error)) return FALSE; break; @@ -866,25 +899,27 @@ teco_interface_handle_key_press(guint keyval, guint state, GError **error) * Control keys and keys with printable representation */ default: { - gunichar u = gdk_keyval_to_unicode(keyval); - - if (!u || g_unichar_to_utf8(u, NULL) != 1) - break; + gunichar u = gdk_keyval_to_unicode(event->keyval); - gchar key; - - g_unichar_to_utf8(u, &key); - if (key > 0x7F) - break; - /* - * NOTE: Alt-Gr key-combinations are sometimes reported as - * Ctrl+Alt, so we filter those out. - */ - if ((state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == GDK_CONTROL_MASK) - key = TECO_CTL_KEY(g_ascii_toupper(key)); - - if (!teco_cmdline_keypress_c(key, error)) - return FALSE; + if (u && u < 0x80 && (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == GDK_CONTROL_MASK) { + /* + * NOTE: Alt-Gr key-combinations are sometimes reported as + * Ctrl+Alt, so we filter those out. + */ + if (!teco_cmdline_keypress_c(TECO_CTL_KEY(g_ascii_toupper(u)), error)) + return FALSE; + } else { + /* + * This is necessary to handle dead keys and in the future + * for inputting Asian languages. + * + * FIXME: We do not yet support preediting. + * It would be easier to forward the event to the Scintilla + * widget and use its existing IM support. + * But this breaks the event freezing and results in flickering. + */ + gtk_im_context_filter_keypress(teco_interface.input_method, event); + } } } @@ -1079,6 +1114,9 @@ teco_interface_cleanup(void) { teco_string_clear(&teco_interface.info_current); + if (teco_interface.input_method) + g_object_unref(teco_interface.input_method); + if (teco_interface.window) gtk_widget_destroy(teco_interface.window); @@ -1177,7 +1215,7 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us gdk_window_freeze_updates(top_window); teco_sigint_occurred = FALSE; - teco_interface_handle_key_press(event->key.keyval, event->key.state, &error); + teco_interface_handle_key_press(&event->key, &error); teco_sigint_occurred = FALSE; gdk_window_thaw_updates(top_window); |