diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-07-30 23:58:32 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-07-31 00:33:43 +0300 |
commit | 2ec568579823c991b919fa3a2c8583a8db21cb81 (patch) | |
tree | 5ee30e3cde1df8b284aec73380c34b79afdbc8ab /src/interface-gtk | |
parent | 86fbf212de71a83e7bb4d83a4b33e54bed52dff9 (diff) | |
download | sciteco-2ec568579823c991b919fa3a2c8583a8db21cb81.tar.gz |
implemented ^T command: allows typing by code and getting characters from stdin or the user
* n:^T always prints bytes (cf. :^A)
* ^T without arguments returns a codepoint or byte from stdin.
In interactive mode, this currentply places a cursor in the message line and waits for a keypress.
Diffstat (limited to 'src/interface-gtk')
-rw-r--r-- | src/interface-gtk/gtk-label.c | 24 | ||||
-rw-r--r-- | src/interface-gtk/gtk-label.h | 2 | ||||
-rw-r--r-- | src/interface-gtk/interface.c | 98 |
3 files changed, 124 insertions, 0 deletions
diff --git a/src/interface-gtk/gtk-label.c b/src/interface-gtk/gtk-label.c index ef370a2..9fc9e76 100644 --- a/src/interface-gtk/gtk-label.c +++ b/src/interface-gtk/gtk-label.c @@ -275,3 +275,27 @@ teco_gtk_label_get_text(TecoGtkLabel *self) { return &self->string; } + +/** + * Signal that a keypress is expected (after executing ^T) + * by printing the first character in reverse. + * + * @fixme This mimics the current Curses implementation. + * Perhaps better show an icon? + */ +void +teco_gtk_label_highlight_getch(TecoGtkLabel *self) +{ + const gchar *plaintext = gtk_label_get_text(GTK_LABEL(self)); + g_assert(plaintext != NULL); + if (!*plaintext || !strcmp(plaintext, "\u258C")) { + gtk_label_set_text(GTK_LABEL(self), "\u258C"); + } else { + PangoAttrList *attribs = gtk_label_get_attributes(GTK_LABEL(self)); + teco_gtk_label_add_highlight_attribs(attribs, + &self->fg, self->fg_alpha, + &self->bg, self->bg_alpha, + 0, 1); + gtk_label_set_attributes(GTK_LABEL(self), attribs); + } +} diff --git a/src/interface-gtk/gtk-label.h b/src/interface-gtk/gtk-label.h index c52d073..3cd4cb9 100644 --- a/src/interface-gtk/gtk-label.h +++ b/src/interface-gtk/gtk-label.h @@ -33,3 +33,5 @@ void teco_gtk_label_parse_string(const gchar *str, gssize len, PangoColor *fg, guint16 fg_alpha, PangoColor *bg, guint16 bg_alpha, PangoAttrList **attribs, gchar **text); + +void teco_gtk_label_highlight_getch(TecoGtkLabel *self); diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index 544ff22..ae1dd74 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -75,6 +75,7 @@ static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer user_data); static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED; +static gchar teco_interface_get_ansi_key(GdkEventKey *event); /** * Interval between polling for keypresses. @@ -423,6 +424,103 @@ teco_interface_msg_clear(void) teco_gtk_label_set_text(TECO_GTK_LABEL(teco_interface.message_widget), "", 0); } +static void +teco_interface_getch_commit_cb(GtkIMContext *context, gchar *str, gpointer user_data) +{ + teco_int_t *cp = user_data; + + /* + * FIXME: What if str contains several characters? + */ + *cp = g_utf8_get_char_validated(str, -1); + g_assert(*cp >= 0); + gtk_main_quit(); +} + +/* + * FIXME: Redundancies with teco_interface_handle_keypress() + * FIXME: Report function keys + */ +static gboolean +teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + teco_int_t *cp = user_data; + + g_assert(event->type == GDK_KEY_PRESS); + + switch (event->key.keyval) { + case GDK_KEY_Escape: *cp = '\e'; break; + case GDK_KEY_BackSpace: *cp = TECO_CTL_KEY('H'); break; + case GDK_KEY_Tab: *cp = '\t'; break; + case GDK_KEY_Return: *cp = '\n'; break; + default: + /* + * NOTE: Alt-Gr key-combinations are sometimes reported as + * Ctrl+Alt, so we filter those out. + */ + if ((event->key.state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == GDK_CONTROL_MASK && + (*cp = teco_interface_get_ansi_key(&event->key))) { + *cp = TECO_CTL_KEY(g_ascii_toupper(*cp)); + switch (*cp) { + case TECO_CTL_KEY('C'): + teco_interrupted = TRUE; + /* fall through */ + case TECO_CTL_KEY('D'): + *cp = -1; + } + break; + } + + gtk_im_context_filter_keypress(teco_interface.input_method, &event->key); + return TRUE; + } + + gtk_main_quit(); + return TRUE; +} + +teco_int_t +teco_interface_getch(gboolean widechar) +{ + if (!gtk_main_level()) + /* batch mode */ + return teco_interface_stdio_getch(widechar); + + teco_int_t cp = -1; + gulong key_handler, commit_handler; + + /* temporarily replace the "key-press-event" and "commit" handlers */ + g_signal_handlers_block_by_func(teco_interface.window, + teco_interface_input_cb, NULL); + key_handler = g_signal_connect(teco_interface.window, "key-press-event", + G_CALLBACK(teco_interface_getch_input_cb), &cp); + g_signal_handlers_block_by_func(teco_interface.input_method, + teco_interface_cmdline_commit_cb, NULL); + commit_handler = g_signal_connect(teco_interface.input_method, "commit", + G_CALLBACK(teco_interface_getch_commit_cb), &cp); + + /* + * Highlights the first character in the label. + * This mimics what the Curses UI does. + * Is there a better way to signal that we expect input? + */ + teco_gtk_label_highlight_getch(TECO_GTK_LABEL(teco_interface.message_widget)); + + GdkWindow *top_window = gdk_window_get_toplevel(gtk_widget_get_window(teco_interface.window)); + gdk_window_thaw_updates(top_window); + + gtk_main(); + + gdk_window_freeze_updates(top_window); + + g_signal_handler_disconnect(teco_interface.input_method, commit_handler); + g_signal_handlers_unblock_by_func(teco_interface.input_method, teco_interface_cmdline_commit_cb, NULL); + g_signal_handler_disconnect(teco_interface.window, key_handler); + g_signal_handlers_unblock_by_func(teco_interface.window, teco_interface_input_cb, NULL); + + return cp; +} + void teco_interface_show_view(teco_view_t *view) { |