From d7cb3da324ae370eac8fd46e1bf9bc91485b3adb Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Wed, 6 Dec 2017 12:49:34 +0100 Subject: [PATCH] clutter: Make ClutterText implement ClutterInputFocus This only applies when the actor is editable. Implementing this interface will allow it to interact with the input method. --- clutter/clutter/clutter-text.c | 131 ++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-text.c b/clutter/clutter/clutter-text.c index b57b3c535..7df53ffef 100644 --- a/clutter/clutter/clutter-text.c +++ b/clutter/clutter/clutter-text.c @@ -63,6 +63,7 @@ #include "clutter-units.h" #include "clutter-paint-volume-private.h" #include "clutter-scriptable.h" +#include "clutter-input-focus.h" /* cursor width in pixels */ #define DEFAULT_CURSOR_SIZE 2 @@ -269,6 +270,7 @@ static const ClutterColor default_selected_text_color = { 0, 0, 0, 255 }; static ClutterAnimatableIface *parent_animatable_iface = NULL; static ClutterScriptableIface *parent_scriptable_iface = NULL; +static void clutter_input_focus_iface_init (ClutterInputFocusInterface *iface); static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); static void clutter_animatable_iface_init (ClutterAnimatableIface *iface); @@ -276,11 +278,91 @@ G_DEFINE_TYPE_WITH_CODE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR, G_ADD_PRIVATE (ClutterText) + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_INPUT_FOCUS, + clutter_input_focus_iface_init) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_iface_init) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE, clutter_animatable_iface_init)); +static void +clutter_text_input_focus_request_surrounding (ClutterInputFocus *focus) +{ + ClutterText *clutter_text = CLUTTER_TEXT (focus); + ClutterTextBuffer *buffer; + const gchar *text; + gint anchor_pos, cursor_pos; + + buffer = clutter_text_get_buffer (clutter_text); + text = clutter_text_buffer_get_text (buffer); + + cursor_pos = clutter_text_get_cursor_position (clutter_text); + if (cursor_pos < 0) + cursor_pos = clutter_text_buffer_get_length (buffer); + + anchor_pos = clutter_text_get_selection_bound (clutter_text); + if (anchor_pos < 0) + anchor_pos = cursor_pos; + + clutter_input_focus_set_surrounding (focus, text, + g_utf8_offset_to_pointer (text, cursor_pos) - text, + g_utf8_offset_to_pointer (text, anchor_pos) - text); +} + +static void +clutter_text_input_focus_delete_surrounding (ClutterInputFocus *focus, + guint offset, + guint len) +{ + ClutterText *clutter_text = CLUTTER_TEXT (focus); + + if (clutter_text_get_editable (clutter_text)) + clutter_text_delete_text (clutter_text, offset, len); +} + +static void +clutter_text_input_focus_commit_text (ClutterInputFocus *focus, + const gchar *text) +{ + ClutterText *clutter_text = CLUTTER_TEXT (focus); + + if (clutter_text_get_editable (clutter_text)) + { + clutter_text_delete_selection (clutter_text); + clutter_text_insert_text (clutter_text, text, + clutter_text_get_cursor_position (clutter_text)); + } +} + +static void +clutter_text_input_focus_set_preedit_text (ClutterInputFocus *focus, + const gchar *preedit_text, + guint cursor_pos) +{ + ClutterText *clutter_text = CLUTTER_TEXT (focus); + + if (clutter_text_get_editable (clutter_text)) + { + PangoAttrList *list; + + list = pango_attr_list_new (); + pango_attr_list_insert (list, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE)); + clutter_text_set_preedit_string (clutter_text, + preedit_text, list, + cursor_pos); + pango_attr_list_unref (list); + } +} + +static void +clutter_input_focus_iface_init (ClutterInputFocusInterface *iface) +{ + iface->request_surrounding = clutter_text_input_focus_request_surrounding; + iface->delete_surrounding = clutter_text_input_focus_delete_surrounding; + iface->commit_text = clutter_text_input_focus_commit_text; + iface->set_preedit_text = clutter_text_input_focus_set_preedit_text; +} + static inline void clutter_text_dirty_paint_volume (ClutterText *text) { @@ -1009,6 +1091,22 @@ clutter_text_position_to_coords (ClutterText *self, return TRUE; } +static inline void +update_cursor_location (ClutterText *self) +{ + ClutterTextPrivate *priv = self->priv; + ClutterRect rect; + float x, y; + + if (!priv->editable) + return; + + rect = priv->cursor_rect; + clutter_actor_get_transformed_position (CLUTTER_ACTOR (self), &x, &y); + clutter_rect_offset (&rect, x, y); + clutter_input_focus_set_cursor_location (CLUTTER_INPUT_FOCUS (self), &rect); +} + static inline void clutter_text_ensure_cursor_position (ClutterText *self) { @@ -1057,6 +1155,8 @@ clutter_text_ensure_cursor_position (ClutterText *self) g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &cursor_pos); g_signal_emit (self, text_signals[CURSOR_CHANGED], 0); + + update_cursor_location (self); } } @@ -2085,9 +2185,11 @@ clutter_text_key_press (ClutterActor *actor, g_assert (pool != NULL); /* we allow passing synthetic events that only contain - * the Unicode value and not the key symbol + * the Unicode value and not the key symbol, unless they + * contain the input method flag. */ - if (event->keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC)) + if (event->keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC) && + !(event->flags & CLUTTER_EVENT_FLAG_INPUT_METHOD)) res = FALSE; else res = clutter_binding_pool_activate (pool, event->keyval, @@ -2105,6 +2207,9 @@ clutter_text_key_press (ClutterActor *actor, { gunichar key_unichar; + if (clutter_input_focus_filter_key_event (CLUTTER_INPUT_FOCUS (actor), event)) + return CLUTTER_EVENT_STOP; + /* Skip keys when control is pressed */ key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event); @@ -2141,6 +2246,16 @@ clutter_text_key_press (ClutterActor *actor, return CLUTTER_EVENT_PROPAGATE; } +static gboolean +clutter_text_key_release (ClutterActor *actor, + ClutterKeyEvent *event) +{ + if (clutter_input_focus_filter_key_event (CLUTTER_INPUT_FOCUS (actor), event)) + return CLUTTER_EVENT_STOP; + + return CLUTTER_EVENT_PROPAGATE; +} + static void clutter_text_compute_layout_offsets (ClutterText *self, PangoLayout *layout, @@ -2664,6 +2779,9 @@ clutter_text_key_focus_in (ClutterActor *actor) { ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + if (priv->editable) + clutter_input_focus_focus_in (CLUTTER_INPUT_FOCUS (actor)); + priv->has_focus = TRUE; clutter_text_queue_redraw (actor); @@ -2676,6 +2794,9 @@ clutter_text_key_focus_out (ClutterActor *actor) priv->has_focus = FALSE; + if (priv->editable) + clutter_input_focus_focus_out (CLUTTER_INPUT_FOCUS (actor)); + clutter_text_queue_redraw (actor); } @@ -3369,6 +3490,7 @@ clutter_text_class_init (ClutterTextClass *klass) actor_class->get_preferred_height = clutter_text_get_preferred_height; actor_class->allocate = clutter_text_allocate; actor_class->key_press_event = clutter_text_key_press; + actor_class->key_release_event = clutter_text_key_release; actor_class->button_press_event = clutter_text_button_press; actor_class->button_release_event = clutter_text_button_release; actor_class->motion_event = clutter_text_motion; @@ -4469,6 +4591,11 @@ clutter_text_set_editable (ClutterText *self, { priv->editable = editable; + if (!priv->editable) + clutter_input_focus_focus_out (CLUTTER_INPUT_FOCUS (self)); + else if (priv->has_focus) + clutter_input_focus_focus_in (CLUTTER_INPUT_FOCUS (self)); + clutter_text_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]);