fixed the delayed selection paint by changing gtk_xtext_render_ents() to queue a GTK redraw when called outside ::draw on GTK3, instead of attempting direct window painting. This addresses the “double-click/drag selection only appears after some other window change” behavior.

Added an in-code comment explaining why this path is needed (GTK3/Wayland frame-driven presentation).
This commit is contained in:
2026-02-17 13:24:42 -07:00
parent 7fd8e5455d
commit 6025195234

View File

@@ -195,6 +195,8 @@ static void gtk_xtext_search_textentry_fini (gpointer, gpointer);
static void gtk_xtext_search_fini (xtext_buffer *);
static gboolean gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr);
static char * gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent, int *ret_off, int *ret_len, GSList **slp);
static gboolean gtk_xtext_word_select_char (const unsigned char *ch);
static gboolean gtk_xtext_get_word_select_range (GtkXText *xtext, int x, int y, textentry **ret_ent, int *ret_off, int *ret_len);
static inline void
gtk_xtext_cursor_unref (GdkCursor *cursor)
@@ -1856,10 +1858,12 @@ gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean ren
if (xtext->word_select)
{
/* a word selection cannot be started if the cursor is out of bounds in gtk_xtext_button_press */
if (!gtk_xtext_get_word_select_range (xtext, low_x, low_y, NULL, &low_offs, NULL))
gtk_xtext_get_word (xtext, low_x, low_y, NULL, &low_offs, NULL, NULL);
/* in case the cursor is out of bounds we keep offset_end from gtk_xtext_find_char and fix the length */
if (gtk_xtext_get_word (xtext, high_x, high_y, NULL, &high_offs, &high_len, NULL) == NULL)
if (!gtk_xtext_get_word_select_range (xtext, high_x, high_y, NULL, &high_offs, &high_len) &&
gtk_xtext_get_word (xtext, high_x, high_y, NULL, &high_offs, &high_len, NULL) == NULL)
high_len = high_offs == high_ent->str_len? 0: -1; /* -1 for the space, 0 if at the end */
high_offs += high_len;
if (low_y < 0)
@@ -2134,6 +2138,57 @@ gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
return word;
}
static gboolean
gtk_xtext_word_select_char (const unsigned char *ch)
{
gunichar uc;
if (!ch || !*ch)
return FALSE;
uc = g_utf8_get_char_validated ((const gchar *)ch, -1);
if (uc == (gunichar)-1 || uc == (gunichar)-2)
return FALSE;
return g_unichar_isalnum (uc) || uc == '_' || uc == '-';
}
static gboolean
gtk_xtext_get_word_select_range (GtkXText *xtext, int x, int y, textentry **ret_ent, int *ret_off, int *ret_len)
{
textentry *ent;
int offset;
unsigned char *start, *end;
ent = gtk_xtext_find_char (xtext, x, y, &offset, NULL);
if (!ent || offset < 0 || offset >= ent->str_len)
return FALSE;
start = ent->str + offset;
end = g_utf8_find_next_char (start, ent->str + ent->str_len);
if (!gtk_xtext_word_select_char (start))
return FALSE;
while (start > ent->str)
{
unsigned char *prev = g_utf8_find_prev_char (ent->str, start);
if (!prev || !gtk_xtext_word_select_char (prev))
break;
start = prev;
}
while (end && end < ent->str + ent->str_len && gtk_xtext_word_select_char (end))
end = g_utf8_find_next_char (end, ent->str + ent->str_len);
if (ret_ent)
*ret_ent = ent;
if (ret_off)
*ret_off = (int)(start - ent->str);
if (ret_len)
*ret_len = (int)(end - start);
return TRUE;
}
static void
gtk_xtext_unrender_hilight (GtkXText *xtext)
{
@@ -2581,7 +2636,8 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
if (event->type == GDK_2BUTTON_PRESS) /* WORD select */
{
gtk_xtext_check_mark_stamp (xtext, mask);
if (gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len, 0))
if (gtk_xtext_get_word_select_range (xtext, x, y, &ent, &offset, &len) ||
gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len, 0))
{
if (len == 0)
return FALSE;
@@ -4324,6 +4380,21 @@ gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
static int
gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb)
{
/*
* On GTK3 (especially Wayland), event handlers are outside ::draw and direct
* window painting may not be presented immediately. Queue a frame instead so
* selections appear right away.
*/
#if HAVE_GTK3
if (xtext->draw_cr == NULL)
{
GtkWidget *w = GTK_WIDGET (xtext);
if (gtk_widget_get_realized (w))
gtk_widget_queue_draw (w);
return 0;
}
#endif
textentry *ent, *orig_ent, *tmp_ent;
int line;
int lines_max;