aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-gtk
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2025-07-30 23:58:32 +0300
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2025-07-31 00:33:43 +0300
commit2ec568579823c991b919fa3a2c8583a8db21cb81 (patch)
tree5ee30e3cde1df8b284aec73380c34b79afdbc8ab /src/interface-gtk
parent86fbf212de71a83e7bb4d83a4b33e54bed52dff9 (diff)
downloadsciteco-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.c24
-rw-r--r--src/interface-gtk/gtk-label.h2
-rw-r--r--src/interface-gtk/interface.c98
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)
{