Commit: patch 9.2.0722: GTK4: find/replace dialog can be improved

0 views
Skip to first unread message

Christian Brabandt

unread,
3:15 PM (1 hour ago) 3:15 PM
to vim...@googlegroups.com
patch 9.2.0722: GTK4: find/replace dialog can be improved

Commit: https://github.com/vim/vim/commit/9ed2c957fee9508c158bbc92b1404e93a2854197
Author: Foxe Chen <chen...@gmail.com>
Date: Wed Jun 24 18:57:35 2026 +0000

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 <chen...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c
index a193233a9..06efcfec1 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] != '
+
+ 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 05a8e6cd7..d9613a8fb 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,
/**/
Reply all
Reply to author
Forward
0 new messages