From 428dafa568923d5632101c716fb20a3de35d27be Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Sat, 15 Feb 2025 01:32:05 +0300 Subject: support mouse interaction with popup windows * Curses allows scrolling with the scroll wheel at least if mouse support is enabled via ED flags. Gtk always supported that. * Allow clicking on popup entries to fully autocomplete them. Since this behavior - just like auto completions - is parser state-dependant, I introduced a new state method (insert_completion_cb). All the implementations are currently in cmdline.c since there is some overlap with the process_edit_cmd_cb implementations. * Fixed pressing undefined function keys while showing the popup. The popup area is no longer redrawn/replaced with the Scintilla view. Instead, continue to show the popup. --- src/cmdline.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 138 insertions(+), 11 deletions(-) (limited to 'src/cmdline.c') diff --git a/src/cmdline.c b/src/cmdline.c index d32df03..dde096d 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -614,7 +614,7 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t * */ if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -644,6 +644,16 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t * return teco_state_process_edit_cmd(parent_ctx, NULL, key, error); } +gboolean +teco_state_stringbuilding_insert_completion(teco_machine_stringbuilding_t *ctx, const teco_string_t *str, GError **error) +{ + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(ctx, str->data, str->len, &str_escaped); + if (!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) + teco_string_append_c(&str_escaped, ' '); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + gboolean teco_state_stringbuilding_escaped_process_edit_cmd(teco_machine_stringbuilding_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) @@ -683,6 +693,14 @@ teco_state_expectstring_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_ return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); } +gboolean +teco_state_expectstring_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + teco_state_t *stringbuilding_current = stringbuilding_ctx->parent.current; + return stringbuilding_current->insert_completion_cb(&stringbuilding_ctx->parent, str, error); +} + gboolean teco_state_insert_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -778,7 +796,7 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -800,6 +818,20 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); } +gboolean +teco_state_expectfile_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + if ((!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) && + ctx->expectstring.nesting == 1) + teco_string_append_wc(&str_escaped, + ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + gboolean teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -820,7 +852,7 @@ teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -854,6 +886,21 @@ teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t return teco_state_expectfile_process_edit_cmd(ctx, parent_ctx, key, error); } +gboolean +teco_state_expectglob_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_autofree gchar *pattern_escaped = teco_globber_escape_pattern(str->data); + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, pattern_escaped, strlen(pattern_escaped), &str_escaped); + if ((!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) && + ctx->expectstring.nesting == 1) + teco_string_append_wc(&str_escaped, + ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + gboolean teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -874,7 +921,7 @@ teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t * if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -897,6 +944,19 @@ teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t * return teco_state_expectfile_process_edit_cmd(ctx, parent_ctx, key, error); } +gboolean +teco_state_expectdir_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + /* + * FIXME: We might terminate the command in case of leaf directories. + */ + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + gboolean teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -909,6 +969,18 @@ teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t return expectqreg_current->process_edit_cmd_cb((teco_machine_t *)ctx->expectqreg, &ctx->parent, key, error); } +gboolean +teco_state_expectqreg_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + g_assert(ctx->expectqreg != NULL); + /* + * NOTE: teco_machine_qregspec_t is private, so we downcast to teco_machine_t. + * Otherwise, we'd have to move this callback into qreg.c. + */ + teco_state_t *expectqreg_current = ((teco_machine_t *)ctx->expectqreg)->current; + return expectqreg_current->insert_completion_cb((teco_machine_t *)ctx->expectqreg, str, error); +} + gboolean teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -919,7 +991,7 @@ teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_ if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -944,6 +1016,12 @@ teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_ return teco_state_process_edit_cmd(parent_ctx, NULL, key, error); } +gboolean +teco_state_qregspec_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, GError **error) +{ + return teco_cmdline_insert(str->data, str->len, error); +} + gboolean teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -967,7 +1045,7 @@ teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_m if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -984,6 +1062,17 @@ teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_m return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, (teco_machine_t *)ctx, key, error); } +gboolean +teco_state_qregspec_string_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = teco_machine_qregspec_get_stringbuilding(ctx); + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + teco_string_append_c(&str_escaped, ']'); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + gboolean teco_state_execute_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -1003,14 +1092,14 @@ teco_state_execute_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa break; /* - * In the EC command, completes files just like ^T + * In the EC command, completes files just like ^G. * * TODO: Implement shell-command completion by iterating * executables in $PATH */ if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1049,7 +1138,7 @@ teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_mac if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1074,6 +1163,17 @@ teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_mac return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); } +gboolean +teco_state_scintilla_symbols_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + teco_string_append_c(&str_escaped, ','); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -1094,7 +1194,7 @@ teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1121,6 +1221,20 @@ teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); } +gboolean +teco_state_goto_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + /* + * FIXME: This does not escape `,`. Cannot be escaped via ^Q currently? + */ + teco_string_append_c(&str_escaped, ','); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + gboolean teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -1141,7 +1255,7 @@ teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1163,6 +1277,19 @@ teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); } +gboolean +teco_state_help_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + if (ctx->expectstring.nesting == 1) + teco_string_append_wc(&str_escaped, + ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + /* * Command states */ -- cgit v1.2.3