Merge branch 'preedit-selection-fix' into 'master'

Preedit selection fix

Closes #1123

See merge request GNOME/gtk!3255
This commit is contained in:
Matthias Clasen 2021-03-04 22:48:25 +00:00
commit c303ec1553
7 changed files with 283 additions and 77 deletions

View File

@ -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
<SUBSECTION Standard>
GTK_IM_CONTEXT

View File

@ -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
* methods 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);

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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",
@ -3305,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);
/*
@ -3334,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);
@ -4150,6 +4160,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,
@ -4200,8 +4217,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;

View File

@ -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)
@ -8259,19 +8270,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;