From 4b9aeaf9e38ef787cadd81149683c9e9416cc605 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 13:27:38 -0500 Subject: [PATCH 1/8] imcontext: Add [gs]et_surrounding_with_selection The Wayland text protocol expects to get both ends of the selection with its surrounding call, so make that available via GtkIMContext. We add this as new api and vfuncs, and fall back, so that existing implementations keep working. --- docs/reference/gtk/gtk4-sections.txt | 2 + gtk/gtkimcontext.c | 172 ++++++++++++++++++++++----- gtk/gtkimcontext.h | 40 +++++-- 3 files changed, 173 insertions(+), 41 deletions(-) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 302d1ad0be..7f20d98ae2 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -1812,6 +1812,8 @@ gtk_im_context_set_cursor_location gtk_im_context_set_use_preedit gtk_im_context_set_surrounding gtk_im_context_get_surrounding +gtk_im_context_set_surrounding_with_selection +gtk_im_context_get_surrounding_with_selection gtk_im_context_delete_surrounding GTK_IM_CONTEXT diff --git a/gtk/gtkimcontext.c b/gtk/gtkimcontext.c index 3080699379..13a8c3bb31 100644 --- a/gtk/gtkimcontext.c +++ b/gtk/gtkimcontext.c @@ -128,13 +128,18 @@ static void gtk_im_context_real_get_preedit_string (GtkIMContext *context, int *cursor_pos); static gboolean gtk_im_context_real_filter_keypress (GtkIMContext *context, GdkEvent *event); -static gboolean gtk_im_context_real_get_surrounding (GtkIMContext *context, - char **text, - int *cursor_index); -static void gtk_im_context_real_set_surrounding (GtkIMContext *context, - const char *text, - int len, - int cursor_index); + +static gboolean gtk_im_context_real_get_surrounding_with_selection + (GtkIMContext *context, + char **text, + int *cursor_index, + int *selection_bound); +static void gtk_im_context_real_set_surrounding_with_selection + (GtkIMContext *context, + const char *text, + int len, + int cursor_index, + int selection_bound); static void gtk_im_context_get_property (GObject *obj, guint property_id, @@ -204,6 +209,18 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkIMContext, gtk_im_context, G_TYPE_OBJECT * behavior. The base implementation emits * #GtkIMContext::retrieve-surrounding and records the context received * by the subsequent invocation of @get_surrounding. + * @set_surrounding_with_selection: Called via gtk_im_context_set_surrounding_with_selection() + * in response to signal #GtkIMContext::retrieve-surrounding to update the input + * method’s idea of the context around the cursor. It is not necessary to + * override this method even with input methods which implement + * context-dependent behavior. The base implementation is sufficient for + * gtk_im_context_get_surrounding() to work. + * @get_surrounding_with_selection: Called via gtk_im_context_get_surrounding_with_selection() + * to update the context around the cursor location. It is not necessary to override + * this method even with input methods which implement context-dependent + * behavior. The base implementation emits + * #GtkIMContext::retrieve-surrounding and records the context received + * by the subsequent invocation of @get_surrounding. */ static void gtk_im_context_class_init (GtkIMContextClass *klass) @@ -215,8 +232,8 @@ gtk_im_context_class_init (GtkIMContextClass *klass) klass->get_preedit_string = gtk_im_context_real_get_preedit_string; klass->filter_keypress = gtk_im_context_real_filter_keypress; - klass->get_surrounding = gtk_im_context_real_get_surrounding; - klass->set_surrounding = gtk_im_context_real_set_surrounding; + klass->get_surrounding_with_selection = gtk_im_context_real_get_surrounding_with_selection; + klass->set_surrounding_with_selection = gtk_im_context_real_set_surrounding_with_selection; /** * GtkIMContext::preedit-start: @@ -381,13 +398,15 @@ typedef struct { char *text; int cursor_index; + int selection_bound; } SurroundingInfo; static void -gtk_im_context_real_set_surrounding (GtkIMContext *context, - const char *text, - int len, - int cursor_index) +gtk_im_context_real_set_surrounding_with_selection (GtkIMContext *context, + const char *text, + int len, + int cursor_index, + int selection_bound) { SurroundingInfo *info = g_object_get_data (G_OBJECT (context), "gtk-im-surrounding-info"); @@ -397,13 +416,15 @@ gtk_im_context_real_set_surrounding (GtkIMContext *context, g_free (info->text); info->text = g_strndup (text, len); info->cursor_index = cursor_index; + info->selection_bound = selection_bound; } } static gboolean -gtk_im_context_real_get_surrounding (GtkIMContext *context, - char **text, - int *cursor_index) +gtk_im_context_real_get_surrounding_with_selection (GtkIMContext *context, + char **text, + int *cursor_index, + int *selection_bound) { gboolean result; gboolean info_is_local = FALSE; @@ -426,11 +447,13 @@ gtk_im_context_real_get_surrounding (GtkIMContext *context, { *text = g_strdup (info->text ? info->text : ""); *cursor_index = info->cursor_index; + *selection_bound = info->selection_bound; } else { *text = NULL; *cursor_index = 0; + *selection_bound = 0; } if (info_is_local) @@ -722,20 +745,47 @@ gtk_im_context_set_use_preedit (GtkIMContext *context, * @text. * @len: the length of @text, or -1 if @text is nul-terminated * @cursor_index: the byte index of the insertion cursor within @text. - * + * * Sets surrounding context around the insertion point and preedit * string. This function is expected to be called in response to the * GtkIMContext::retrieve_surrounding signal, and will likely have no * effect if called at other times. - **/ + * + * Deprecated: 4.2: Use gtk_im_context_set_surrounding_with_selection() instead + */ void gtk_im_context_set_surrounding (GtkIMContext *context, - const char *text, - int len, - int cursor_index) + const char *text, + int len, + int cursor_index) +{ + gtk_im_context_set_surrounding_with_selection (context, text, len, cursor_index, cursor_index); +} + +/** + * gtk_im_context_set_surrounding_with_selection: + * @context: a #GtkIMContext + * @text: text surrounding the insertion point, as UTF-8. + * the preedit string should not be included within + * @text. + * @len: the length of @text, or -1 if @text is nul-terminated + * @cursor_index: the byte index of the insertion cursor within @text. + * @anchor_index: the byte index of the selection bound within @text + * + * Sets surrounding context around the insertion point and preedit + * string. This function is expected to be called in response to the + * GtkIMContext::retrieve_surrounding signal, and will likely have no + * effect if called at other times. + */ +void +gtk_im_context_set_surrounding_with_selection (GtkIMContext *context, + const char *text, + int len, + int cursor_index, + int anchor_index) { GtkIMContextClass *klass; - + g_return_if_fail (GTK_IS_IM_CONTEXT (context)); g_return_if_fail (text != NULL || len == 0); @@ -747,7 +797,9 @@ gtk_im_context_set_surrounding (GtkIMContext *context, g_return_if_fail (cursor_index >= 0 && cursor_index <= len); klass = GTK_IM_CONTEXT_GET_CLASS (context); - if (klass->set_surrounding) + if (klass->set_surrounding_with_selection) + klass->set_surrounding_with_selection (context, text, len, cursor_index, anchor_index); + else if (klass->set_surrounding) klass->set_surrounding (context, text, len, cursor_index); } @@ -760,7 +812,7 @@ gtk_im_context_set_surrounding (GtkIMContext *context, * stored in this location with g_free(). * @cursor_index: (out): location to store byte index of the insertion * cursor within @text. - * + * * Retrieves context around the insertion point. Input methods * typically want context in order to constrain input text based on * existing text; this is important for languages such as Thai where @@ -776,24 +828,78 @@ gtk_im_context_set_surrounding (GtkIMContext *context, * * Returns: %TRUE if surrounding text was provided; in this case * you must free the result stored in *text. - **/ + * + * Deprecated: 4.2: Use gtk_im_context_get_surrounding_with_selection() instead. + */ gboolean -gtk_im_context_get_surrounding (GtkIMContext *context, - char **text, - int *cursor_index) +gtk_im_context_get_surrounding (GtkIMContext *context, + char **text, + int *cursor_index) +{ + return gtk_im_context_get_surrounding_with_selection (context, + text, + cursor_index, + NULL); +} + +/** + * gtk_im_context_get_surrounding: + * @context: a #GtkIMContext + * @text: (out) (transfer full): location to store a UTF-8 encoded + * string of text holding context around the insertion point. + * If the function returns %TRUE, then you must free the result + * stored in this location with g_free(). + * @cursor_index: (out): location to store byte index of the insertion + * cursor within @text. + * @anchor_index: (out): location to store byte index of the selection + * bound within @text + * + * Retrieves context around the insertion point. Input methods + * typically want context in order to constrain input text based on + * existing text; this is important for languages such as Thai where + * only some sequences of characters are allowed. + * + * This function is implemented by emitting the + * GtkIMContext::retrieve_surrounding signal on the input method; in + * response to this signal, a widget should provide as much context as + * is available, up to an entire paragraph, by calling + * gtk_im_context_set_surrounding(). Note that there is no obligation + * for a widget to respond to the ::retrieve_surrounding signal, so input + * methods must be prepared to function without context. + * + * Returns: %TRUE if surrounding text was provided; in this case + * you must free the result stored in *text. + */ +gboolean +gtk_im_context_get_surrounding_with_selection (GtkIMContext *context, + char **text, + int *cursor_index, + int *anchor_index) { GtkIMContextClass *klass; char *local_text = NULL; int local_index; gboolean result = FALSE; - + g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE); klass = GTK_IM_CONTEXT_GET_CLASS (context); - if (klass->get_surrounding) - result = klass->get_surrounding (context, - text ? text : &local_text, - cursor_index ? cursor_index : &local_index); + if (klass->get_surrounding_with_selection) + result = klass->get_surrounding_with_selection + (context, + text ? text : &local_text, + cursor_index ? cursor_index : &local_index, + anchor_index ? anchor_index : &local_index); + else if (klass->get_surrounding) + { + result = klass->get_surrounding (context, + text ? text : &local_text, + &local_index); + if (cursor_index) + *cursor_index = local_index; + if (anchor_index) + *anchor_index = local_index; + } if (result) g_free (local_text); diff --git a/gtk/gtkimcontext.h b/gtk/gtkimcontext.h index 17419eb180..c9558f2ba3 100644 --- a/gtk/gtkimcontext.h +++ b/gtk/gtkimcontext.h @@ -83,6 +83,18 @@ struct _GtkIMContextClass gboolean (*get_surrounding) (GtkIMContext *context, char **text, int *cursor_index); + void (*set_surrounding_with_selection) + (GtkIMContext *context, + const char *text, + int len, + int cursor_index, + int anchor_index); + gboolean (*get_surrounding_with_selection) + (GtkIMContext *context, + char **text, + int *cursor_index, + int *anchor_index); + /*< private >*/ /* Padding for future expansion */ void (*_gtk_reserved1) (void); @@ -90,7 +102,6 @@ struct _GtkIMContextClass void (*_gtk_reserved3) (void); void (*_gtk_reserved4) (void); void (*_gtk_reserved5) (void); - void (*_gtk_reserved6) (void); }; GDK_AVAILABLE_IN_ALL @@ -130,15 +141,28 @@ void gtk_im_context_set_cursor_location (GtkIMContext *context, GDK_AVAILABLE_IN_ALL void gtk_im_context_set_use_preedit (GtkIMContext *context, gboolean use_preedit); -GDK_AVAILABLE_IN_ALL +GDK_DEPRECATED_IN_4_2_FOR(gtk_im_context_set_surrounding_with_selection) void gtk_im_context_set_surrounding (GtkIMContext *context, - const char *text, - int len, - int cursor_index); -GDK_AVAILABLE_IN_ALL + const char *text, + int len, + int cursor_index); +GDK_DEPRECATED_IN_4_2_FOR(gtk_im_context_get_surrounding_with_selection) gboolean gtk_im_context_get_surrounding (GtkIMContext *context, - char **text, - int *cursor_index); + char **text, + int *cursor_index); +GDK_AVAILABLE_IN_ALL +void gtk_im_context_set_surrounding_with_selection + (GtkIMContext *context, + const char *text, + int len, + int cursor_index, + int anchor_index); +GDK_AVAILABLE_IN_ALL +gboolean gtk_im_context_get_surrounding_with_selection + (GtkIMContext *context, + char **text, + int *cursor_index, + int *anchor_index); GDK_AVAILABLE_IN_ALL gboolean gtk_im_context_delete_surrounding (GtkIMContext *context, int offset, From 97db959305cd30450dfa1dca713ee7477b792fa4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 15:14:52 -0500 Subject: [PATCH 2/8] immulticontext: Implement [gs]et_surrounding_with_selection This is the recommended api, going forward. --- gtk/gtkimmulticontext.c | 48 ++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/gtk/gtkimmulticontext.c b/gtk/gtkimmulticontext.c index 75a70c3f0d..5775a42880 100644 --- a/gtk/gtkimmulticontext.c +++ b/gtk/gtkimmulticontext.c @@ -74,13 +74,17 @@ static void gtk_im_multicontext_set_cursor_location (GtkIMContext GdkRectangle *area); static void gtk_im_multicontext_set_use_preedit (GtkIMContext *context, gboolean use_preedit); -static gboolean gtk_im_multicontext_get_surrounding (GtkIMContext *context, - char **text, - int *cursor_index); -static void gtk_im_multicontext_set_surrounding (GtkIMContext *context, - const char *text, - int len, - int cursor_index); +static gboolean gtk_im_multicontext_get_surrounding_with_selection + (GtkIMContext *context, + char **text, + int *cursor_index, + int *anchor_index); +static void gtk_im_multicontext_set_surrounding_with_selection + (GtkIMContext *context, + const char *text, + int len, + int cursor_index, + int anchor_index); static void gtk_im_multicontext_preedit_start_cb (GtkIMContext *delegate, GtkIMMulticontext *multicontext); @@ -118,8 +122,8 @@ gtk_im_multicontext_class_init (GtkIMMulticontextClass *class) im_context_class->reset = gtk_im_multicontext_reset; im_context_class->set_cursor_location = gtk_im_multicontext_set_cursor_location; im_context_class->set_use_preedit = gtk_im_multicontext_set_use_preedit; - im_context_class->set_surrounding = gtk_im_multicontext_set_surrounding; - im_context_class->get_surrounding = gtk_im_multicontext_get_surrounding; + im_context_class->set_surrounding_with_selection = gtk_im_multicontext_set_surrounding_with_selection; + im_context_class->get_surrounding_with_selection = gtk_im_multicontext_get_surrounding_with_selection; gobject_class->finalize = gtk_im_multicontext_finalize; } @@ -460,37 +464,41 @@ gtk_im_multicontext_set_use_preedit (GtkIMContext *context, } static gboolean -gtk_im_multicontext_get_surrounding (GtkIMContext *context, - char **text, - int *cursor_index) +gtk_im_multicontext_get_surrounding_with_selection (GtkIMContext *context, + char **text, + int *cursor_index, + int *anchor_index) { GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context); GtkIMContext *delegate = gtk_im_multicontext_get_delegate (multicontext); if (delegate) - return gtk_im_context_get_surrounding (delegate, text, cursor_index); + return gtk_im_context_get_surrounding_with_selection (delegate, text, cursor_index, anchor_index); else { if (text) - *text = NULL; + *text = NULL; if (cursor_index) - *cursor_index = 0; + *cursor_index = 0; + if (anchor_index) + *anchor_index = 0; return FALSE; } } static void -gtk_im_multicontext_set_surrounding (GtkIMContext *context, - const char *text, - int len, - int cursor_index) +gtk_im_multicontext_set_surrounding_with_selection (GtkIMContext *context, + const char *text, + int len, + int cursor_index, + int anchor_index) { GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context); GtkIMContext *delegate = gtk_im_multicontext_get_delegate (multicontext); if (delegate) - gtk_im_context_set_surrounding (delegate, text, len, cursor_index); + gtk_im_context_set_surrounding_with_selection (delegate, text, len, cursor_index, anchor_index); } static void From 35fceca157fdc6f49ace15db31a78cf779a9112f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 13:30:55 -0500 Subject: [PATCH 3/8] imwayland: Pass anchor with surrounding This was missing in the initial implementation since GtkIMContext did not have it in its api, but it was added now, so we can send the information. --- gtk/gtkimcontextwayland.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/gtk/gtkimcontextwayland.c b/gtk/gtkimcontextwayland.c index 0bee5162d9..b380a8a150 100644 --- a/gtk/gtkimcontextwayland.c +++ b/gtk/gtkimcontextwayland.c @@ -909,7 +909,8 @@ static void gtk_im_context_wayland_set_surrounding (GtkIMContext *context, const char *text, int len, - int cursor_index) + int cursor_index, + int selection_bound) { GtkIMContextWayland *context_wayland; @@ -918,8 +919,7 @@ gtk_im_context_wayland_set_surrounding (GtkIMContext *context, g_free (context_wayland->surrounding.text); context_wayland->surrounding.text = g_strndup (text, len); context_wayland->surrounding.cursor_idx = cursor_index; - /* Anchor is not exposed via the set_surrounding interface, emulating. */ - context_wayland->surrounding.anchor_idx = cursor_index; + context_wayland->surrounding.anchor_idx = selection_bound; notify_surrounding_text (context_wayland); /* State changes coming from reset don't have any other opportunity to get @@ -932,7 +932,8 @@ gtk_im_context_wayland_set_surrounding (GtkIMContext *context, static gboolean gtk_im_context_wayland_get_surrounding (GtkIMContext *context, char **text, - int *cursor_index) + int *cursor_index, + int *selection_bound) { GtkIMContextWayland *context_wayland; @@ -943,6 +944,7 @@ gtk_im_context_wayland_get_surrounding (GtkIMContext *context, *text = context_wayland->surrounding.text; *cursor_index = context_wayland->surrounding.cursor_idx; + *selection_bound = context_wayland->surrounding.anchor_idx; return TRUE; } @@ -962,8 +964,8 @@ gtk_im_context_wayland_class_init (GtkIMContextWaylandClass *klass) im_context_class->reset = gtk_im_context_wayland_reset; im_context_class->set_cursor_location = gtk_im_context_wayland_set_cursor_location; im_context_class->set_use_preedit = gtk_im_context_wayland_set_use_preedit; - im_context_class->set_surrounding = gtk_im_context_wayland_set_surrounding; - im_context_class->get_surrounding = gtk_im_context_wayland_get_surrounding; + im_context_class->set_surrounding_with_selection = gtk_im_context_wayland_set_surrounding; + im_context_class->get_surrounding_with_selection = gtk_im_context_wayland_get_surrounding; } static void From feccdad9d2afcda75d87153c48d3bc0324fa4bd8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 14:48:28 -0500 Subject: [PATCH 4/8] text: Send anchor with surrounding We have the api to do it now. --- gtk/gtktext.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 3984750034..f2f1f854b0 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -4200,8 +4200,9 @@ gtk_text_retrieve_surrounding_cb (GtkIMContext *context, /* XXXX ??? does this even make sense when text is not visible? Should we return FALSE? */ text = gtk_text_get_display_text (self, 0, -1); - gtk_im_context_set_surrounding (context, text, strlen (text), /* Length in bytes */ - g_utf8_offset_to_pointer (text, priv->current_pos) - text); + gtk_im_context_set_surrounding_with_selection (context, text, strlen (text), /* Length in bytes */ + g_utf8_offset_to_pointer (text, priv->current_pos) - text, + g_utf8_offset_to_pointer (text, priv->selection_bound) - text); g_free (text); return TRUE; From 447203ce525ff95e916c6106818df2836c7d9b1f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 14:49:14 -0500 Subject: [PATCH 5/8] text: Delete selection when preedit starts This prevents confusing misrendering when the preedit text pushes the selection text out of the rendered selection. Fixes: #1123 --- gtk/gtktext.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gtk/gtktext.c b/gtk/gtktext.c index f2f1f854b0..7791029e52 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -418,6 +418,8 @@ static void direction_changed (GdkDevice *keyboard, static void gtk_text_commit_cb (GtkIMContext *context, const char *str, GtkText *self); +static void gtk_text_preedit_start_cb (GtkIMContext *context, + GtkText *self); static void gtk_text_preedit_changed_cb (GtkIMContext *context, GtkText *self); static gboolean gtk_text_retrieve_surrounding_cb (GtkIMContext *context, @@ -1843,6 +1845,8 @@ gtk_text_init (GtkText *self) */ priv->im_context = gtk_im_multicontext_new (); + g_signal_connect (priv->im_context, "preedit-start", + G_CALLBACK (gtk_text_preedit_start_cb), self); g_signal_connect (priv->im_context, "commit", G_CALLBACK (gtk_text_commit_cb), self); g_signal_connect (priv->im_context, "preedit-changed", @@ -4150,6 +4154,13 @@ direction_changed (GdkDevice *device, /* IM Context Callbacks */ +static void +gtk_text_preedit_start_cb (GtkIMContext *context, + GtkText *self) +{ + gtk_text_delete_selection (self); +} + static void gtk_text_commit_cb (GtkIMContext *context, const char *str, From ccae0e1732d12ca312960ac83681d4d5f8aad1ea Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 14:14:09 -0500 Subject: [PATCH 6/8] text: Optimize away 0-change insertions and deletions We can short-circuit insert and delete calls that are not causing any change. --- gtk/gtktext.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 7791029e52..20c7fee841 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -3309,6 +3309,9 @@ gtk_text_insert_text (GtkText *self, int n_inserted; int n_chars; + if (length == 0) + return; + n_chars = g_utf8_strlen (text, length); /* @@ -3338,6 +3341,9 @@ gtk_text_delete_text (GtkText *self, { GtkTextPrivate *priv = gtk_text_get_instance_private (self); + if (start_pos == end_pos) + return; + begin_change (self); gtk_entry_buffer_delete_text (get_buffer (self), start_pos, end_pos - start_pos); From d1fbfb9c4ed5ebf456f8bbcf16b6ba3a5e2e796a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 14:46:46 -0500 Subject: [PATCH 7/8] textview: Send anchor with surrounding We have the api to do it now. --- gtk/gtktextview.c | 49 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index bece4d5839..c3289a0ce8 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -8259,19 +8259,54 @@ gtk_text_view_retrieve_surrounding_handler (GtkIMContext *context, { GtkTextIter start; GtkTextIter end; - int pos; + GtkTextIter start1; + GtkTextIter end1; + int cursor_pos; + int anchor_pos; char *text; + char *pre; + char *sel; + char *post; + gboolean flip; gtk_text_buffer_get_iter_at_mark (text_view->priv->buffer, &start, - gtk_text_buffer_get_insert (text_view->priv->buffer)); - end = start; + gtk_text_buffer_get_insert (text_view->priv->buffer)); + gtk_text_buffer_get_iter_at_mark (text_view->priv->buffer, &end, + gtk_text_buffer_get_selection_bound (text_view->priv->buffer)); + + flip = gtk_text_iter_compare (&start, &end) < 0; + + gtk_text_iter_order (&start, &end); + + start1 = start; + end1 = end; - pos = gtk_text_iter_get_line_index (&start); gtk_text_iter_set_line_offset (&start, 0); - gtk_text_iter_forward_to_line_end (&end); + gtk_text_iter_forward_to_line_end (&end1); + + pre = gtk_text_iter_get_slice (&start1, &start); + sel = gtk_text_iter_get_slice (&start, &end); + post = gtk_text_iter_get_slice (&end, &end1); + + if (flip) + { + anchor_pos = strlen (pre); + cursor_pos = anchor_pos + strlen (sel); + } + else + { + cursor_pos = strlen (pre); + anchor_pos = cursor_pos + strlen (sel); + } + + text = g_strconcat (pre, sel, post, NULL); + + g_free (pre); + g_free (sel); + g_free (post); + + gtk_im_context_set_surrounding_with_selection (context, text, -1, cursor_pos, anchor_pos); - text = gtk_text_iter_get_slice (&start, &end); - gtk_im_context_set_surrounding (context, text, -1, pos); g_free (text); return TRUE; From d28df0b87e1422a0491970c1b0f9c9bcc5e7689c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 4 Mar 2021 14:55:40 -0500 Subject: [PATCH 8/8] textview: Delete selection when preedit starts This prevents confusing misrendering when the preedit text pushes the selection text out of the rendered selection. Fixes: #1123 --- gtk/gtktextview.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index c3289a0ce8..5b8bca680a 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -504,8 +504,10 @@ static void gtk_text_view_commit_handler (GtkIMContext *conte GtkTextView *text_view); static void gtk_text_view_commit_text (GtkTextView *text_view, const char *text); +static void gtk_text_view_preedit_start_handler (GtkIMContext *context, + GtkTextView *text_view); static void gtk_text_view_preedit_changed_handler (GtkIMContext *context, - GtkTextView *text_view); + GtkTextView *text_view); static gboolean gtk_text_view_retrieve_surrounding_handler (GtkIMContext *context, GtkTextView *text_view); static gboolean gtk_text_view_delete_surrounding_handler (GtkIMContext *context, @@ -1883,6 +1885,8 @@ gtk_text_view_init (GtkTextView *text_view) g_signal_connect (priv->im_context, "commit", G_CALLBACK (gtk_text_view_commit_handler), text_view); + g_signal_connect (priv->im_context, "preedit-start", + G_CALLBACK (gtk_text_view_preedit_start_handler), text_view); g_signal_connect (priv->im_context, "preedit-changed", G_CALLBACK (gtk_text_view_preedit_changed_handler), text_view); g_signal_connect (priv->im_context, "retrieve-surrounding", @@ -8212,6 +8216,13 @@ gtk_text_view_commit_text (GtkTextView *text_view, gtk_text_buffer_get_insert (get_buffer (text_view))); } +static void +gtk_text_view_preedit_start_handler (GtkIMContext *context, + GtkTextView *self) +{ + gtk_text_buffer_delete_selection (self->priv->buffer, TRUE, self->priv->editable); +} + static void gtk_text_view_preedit_changed_handler (GtkIMContext *context, GtkTextView *text_view)