From: Foxe Chen Date: Wed, 24 Jun 2026 18:57:35 +0000 (+0000) Subject: patch 9.2.0722: GTK4: find/replace dialog can be improved X-Git-Tag: v9.2.0722^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9ed2c957fee9508c158bbc92b1404e93a2854197;p=thirdparty%2Fvim.git patch 9.2.0722: GTK4: find/replace dialog can be improved Problem: GTK4: find/replace dialog can be improved Solution: Store the action buttons in the dialog struct, update their sensitivity when the search field changes, close the dialog on Escape and set the dialog's default widget (Foxe Chen) closes: #20613 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index a193233a90..06efcfec15 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -5297,6 +5297,9 @@ typedef struct GtkWidget *mcase; // Match case check GtkWidget *up; // Direction up radio GtkWidget *down; // Direction down radio + GtkWidget *find; // 'Find Next' action button + GtkWidget *replace; // 'Replace With' action button + GtkWidget *all; // 'Replace All' action button } SharedFindReplace; static SharedFindReplace find_widgets = {0}; @@ -5347,6 +5350,61 @@ dialog_destroyed_cb(GtkWidget *widget UNUSED, gpointer data) *(GtkWidget **)data = NULL; } + static void +entry_changed_cb(GtkWidget *entry, GtkWidget *dialog) +{ + const gchar *entry_text; + gboolean nonempty; + + entry_text = gtk_editable_get_text(GTK_EDITABLE(entry)); + + if (!entry_text) + return; // Shouldn't happen + + nonempty = (entry_text[0] != '\0'); + + if (dialog == find_widgets.dialog) + gtk_widget_set_sensitive(find_widgets.find, nonempty); + + if (dialog == repl_widgets.dialog) + { + gtk_widget_set_sensitive(repl_widgets.find, nonempty); + gtk_widget_set_sensitive(repl_widgets.replace, nonempty); + gtk_widget_set_sensitive(repl_widgets.all, nonempty); + } +} + + static gboolean +find_key_pressed_cb( + GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + SharedFindReplace *frdp) +{ + // If the user is holding one of the key modifiers we will just bail out, + // thus preserving the possibility of normal focus traversal. + if (state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) + return FALSE; + + // the Escape key synthesizes a cancellation action + if (keyval == GDK_KEY_Escape) + { + // Destroy rather than hide the dialog: reusing a hidden toplevel loses + // window decorations on Wayland + gtk_window_destroy(GTK_WINDOW(frdp->dialog)); + return TRUE; + } + + return FALSE; +} + + static void +entry_activate_cb(GtkWidget *widget UNUSED, void *udata) +{ + gtk_widget_grab_focus(GTK_WIDGET(udata)); +} + static void find_replace_dialog_create(char_u *arg, int do_replace) { @@ -5356,6 +5414,7 @@ find_replace_dialog_create(char_u *arg, int do_replace) int mcase = !p_ic; GtkWidget *vertbox, *grid, *hbox, *tmp, *btn; gboolean sensitive; + GtkEventController *key_controller; frdp = do_replace ? &repl_widgets : &find_widgets; entry_text = get_find_dialog_text(arg, &wword, &mcase); @@ -5368,7 +5427,7 @@ find_replace_dialog_create(char_u *arg, int do_replace) } // If the dialog already exists, just raise it. - if (frdp->dialog) + if (frdp->dialog != NULL) { if (entry_text != NULL) { @@ -5380,7 +5439,15 @@ find_replace_dialog_create(char_u *arg, int do_replace) (gboolean)mcase); } gtk_window_present(GTK_WINDOW(frdp->dialog)); + + // For :promptfind dialog, always give keyboard focus to 'what' entry. + // For :promptrepl dialog, give it to 'with' entry if 'what' has a + // non-empty entry; otherwise, to 'what' entry. gtk_widget_grab_focus(frdp->what); + if (do_replace && g_utf8_strlen( + gtk_editable_get_text(GTK_EDITABLE(frdp->what)), -1) > 0) + gtk_widget_grab_focus(frdp->with); + vim_free(entry_text); return; } @@ -5392,7 +5459,7 @@ find_replace_dialog_create(char_u *arg, int do_replace) gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE); gtk_window_set_title(GTK_WINDOW(frdp->dialog), do_replace ? _("VIM - Search and Replace...") - : _("VIM - Search...")); + : _("VIM - Search...")); gtk_window_set_resizable(GTK_WINDOW(frdp->dialog), FALSE); g_signal_connect(frdp->dialog, "destroy", @@ -5433,7 +5500,17 @@ find_replace_dialog_create(char_u *arg, int do_replace) frdp->with = gtk_entry_new(); gtk_widget_set_hexpand(frdp->with, TRUE); gtk_grid_attach(GTK_GRID(grid), frdp->with, 1, 1, 1, 1); + gtk_entry_set_activates_default(GTK_ENTRY(frdp->with), TRUE); + + // Make the entry activation only change the input focus onto the + // with item. + gtk_entry_set_activates_default(GTK_ENTRY(frdp->what), FALSE); + g_signal_connect(G_OBJECT(frdp->what), "activate", + G_CALLBACK(entry_activate_cb), frdp->with); } + else + // Make the entry activation do the search. + gtk_entry_set_activates_default(GTK_ENTRY(frdp->what), TRUE); // Checkboxes hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); @@ -5472,6 +5549,18 @@ find_replace_dialog_create(char_u *arg, int do_replace) btn = gtk_button_new_with_label(_("Find Next")); gtk_widget_set_sensitive(btn, sensitive); + frdp->find = btn; + + key_controller = gtk_event_controller_key_new(); + g_signal_connect(key_controller, "key-pressed", + G_CALLBACK(find_key_pressed_cb), frdp); + gtk_widget_add_controller(GTK_WIDGET(frdp->dialog), key_controller); + + g_signal_connect(G_OBJECT(frdp->what), "changed", + G_CALLBACK(entry_changed_cb), frdp->dialog); + + // Make it so that when entry is activated, this button will be activated. + gtk_window_set_default_widget(GTK_WINDOW(frdp->dialog), btn); g_signal_connect(btn, "clicked", G_CALLBACK(find_replace_cb), GINT_TO_POINTER(do_replace ? FRD_R_FINDNEXT : FRD_FINDNEXT)); gtk_box_append(GTK_BOX(hbox), btn); @@ -5479,13 +5568,17 @@ find_replace_dialog_create(char_u *arg, int do_replace) if (do_replace) { btn = gtk_button_new_with_label(_("Replace")); + gtk_widget_set_sensitive(btn, sensitive); g_signal_connect(btn, "clicked", G_CALLBACK(find_replace_cb), GINT_TO_POINTER(FRD_REPLACE)); + frdp->replace = btn; gtk_box_append(GTK_BOX(hbox), btn); btn = gtk_button_new_with_label(_("Replace All")); + gtk_widget_set_sensitive(btn, sensitive); g_signal_connect(btn, "clicked", G_CALLBACK(find_replace_cb), GINT_TO_POINTER(FRD_REPLACEALL)); + frdp->all = btn; gtk_box_append(GTK_BOX(hbox), btn); } @@ -5494,11 +5587,6 @@ find_replace_dialog_create(char_u *arg, int do_replace) G_CALLBACK(gtk_window_destroy), frdp->dialog); gtk_box_append(GTK_BOX(hbox), btn); - // Connect Enter key in entry to Find Next - g_signal_connect_swapped(frdp->what, "activate", - G_CALLBACK(find_replace_cb), - GINT_TO_POINTER(do_replace ? FRD_R_FINDNEXT : FRD_FINDNEXT)); - gtk_window_present(GTK_WINDOW(frdp->dialog)); gtk_widget_grab_focus(frdp->what); if (do_replace && entry_text != NULL && entry_text[0] != NUL) diff --git a/src/version.c b/src/version.c index 05a8e6cd7a..d9613a8fb9 100644 --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 722, /**/ 721, /**/