clutter: Implement ClutterInputFocus subclass for ClutterText
This only applies when the actor is editable. This object will allow editable ClutterText instances to interact with the input method.
This commit is contained in:
parent
aa6561a3b1
commit
3a914a915e
@ -63,6 +63,7 @@
|
|||||||
#include "clutter-units.h"
|
#include "clutter-units.h"
|
||||||
#include "clutter-paint-volume-private.h"
|
#include "clutter-paint-volume-private.h"
|
||||||
#include "clutter-scriptable.h"
|
#include "clutter-scriptable.h"
|
||||||
|
#include "clutter-input-focus.h"
|
||||||
|
|
||||||
/* cursor width in pixels */
|
/* cursor width in pixels */
|
||||||
#define DEFAULT_CURSOR_SIZE 2
|
#define DEFAULT_CURSOR_SIZE 2
|
||||||
@ -98,6 +99,12 @@ struct _LayoutCache
|
|||||||
guint age;
|
guint age;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _ClutterTextInputFocus
|
||||||
|
{
|
||||||
|
ClutterInputFocus parent_instance;
|
||||||
|
ClutterText *text;
|
||||||
|
};
|
||||||
|
|
||||||
struct _ClutterTextPrivate
|
struct _ClutterTextPrivate
|
||||||
{
|
{
|
||||||
PangoFontDescription *font_desc;
|
PangoFontDescription *font_desc;
|
||||||
@ -176,6 +183,8 @@ struct _ClutterTextPrivate
|
|||||||
/* Signal handler for when the :text-direction changes */
|
/* Signal handler for when the :text-direction changes */
|
||||||
guint direction_changed_id;
|
guint direction_changed_id;
|
||||||
|
|
||||||
|
ClutterInputFocus *input_focus;
|
||||||
|
|
||||||
/* bitfields */
|
/* bitfields */
|
||||||
guint alignment : 2;
|
guint alignment : 2;
|
||||||
guint wrap : 1;
|
guint wrap : 1;
|
||||||
@ -269,6 +278,111 @@ static const ClutterColor default_selected_text_color = { 0, 0, 0, 255 };
|
|||||||
static ClutterAnimatableIface *parent_animatable_iface = NULL;
|
static ClutterAnimatableIface *parent_animatable_iface = NULL;
|
||||||
static ClutterScriptableIface *parent_scriptable_iface = NULL;
|
static ClutterScriptableIface *parent_scriptable_iface = NULL;
|
||||||
|
|
||||||
|
/* ClutterTextInputFocus */
|
||||||
|
#define CLUTTER_TYPE_TEXT_INPUT_FOCUS (clutter_text_input_focus_get_type ())
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE (ClutterTextInputFocus, clutter_text_input_focus,
|
||||||
|
CLUTTER, TEXT_INPUT_FOCUS, ClutterInputFocus)
|
||||||
|
G_DEFINE_TYPE (ClutterTextInputFocus, clutter_text_input_focus,
|
||||||
|
CLUTTER_TYPE_INPUT_FOCUS)
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_text_input_focus_request_surrounding (ClutterInputFocus *focus)
|
||||||
|
{
|
||||||
|
ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text;
|
||||||
|
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_INPUT_FOCUS (focus)->text;
|
||||||
|
|
||||||
|
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_INPUT_FOCUS (focus)->text;
|
||||||
|
|
||||||
|
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_INPUT_FOCUS (focus)->text;
|
||||||
|
|
||||||
|
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_text_input_focus_class_init (ClutterTextInputFocusClass *klass)
|
||||||
|
{
|
||||||
|
ClutterInputFocusClass *focus_class = CLUTTER_INPUT_FOCUS_CLASS (klass);
|
||||||
|
|
||||||
|
focus_class->request_surrounding = clutter_text_input_focus_request_surrounding;
|
||||||
|
focus_class->delete_surrounding = clutter_text_input_focus_delete_surrounding;
|
||||||
|
focus_class->commit_text = clutter_text_input_focus_commit_text;
|
||||||
|
focus_class->set_preedit_text = clutter_text_input_focus_set_preedit_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_text_input_focus_init (ClutterTextInputFocus *focus)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static ClutterInputFocus *
|
||||||
|
clutter_text_input_focus_new (ClutterText *text)
|
||||||
|
{
|
||||||
|
ClutterTextInputFocus *focus;
|
||||||
|
|
||||||
|
focus = g_object_new (CLUTTER_TYPE_TEXT_INPUT_FOCUS, NULL);
|
||||||
|
focus->text = text;
|
||||||
|
|
||||||
|
return CLUTTER_INPUT_FOCUS (focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ClutterText */
|
||||||
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
|
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
|
||||||
static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
|
static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
|
||||||
|
|
||||||
@ -1009,6 +1123,22 @@ clutter_text_position_to_coords (ClutterText *self,
|
|||||||
return TRUE;
|
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 (priv->input_focus, &rect);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
clutter_text_ensure_cursor_position (ClutterText *self)
|
clutter_text_ensure_cursor_position (ClutterText *self)
|
||||||
{
|
{
|
||||||
@ -1057,6 +1187,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_EVENT], 0, &cursor_pos);
|
||||||
|
|
||||||
g_signal_emit (self, text_signals[CURSOR_CHANGED], 0);
|
g_signal_emit (self, text_signals[CURSOR_CHANGED], 0);
|
||||||
|
|
||||||
|
update_cursor_location (self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1503,6 +1635,8 @@ clutter_text_finalize (GObject *gobject)
|
|||||||
clutter_text_set_buffer (self, NULL);
|
clutter_text_set_buffer (self, NULL);
|
||||||
g_free (priv->font_name);
|
g_free (priv->font_name);
|
||||||
|
|
||||||
|
g_clear_object (&priv->input_focus);
|
||||||
|
|
||||||
G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
|
G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2085,9 +2219,11 @@ clutter_text_key_press (ClutterActor *actor,
|
|||||||
g_assert (pool != NULL);
|
g_assert (pool != NULL);
|
||||||
|
|
||||||
/* we allow passing synthetic events that only contain
|
/* 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;
|
res = FALSE;
|
||||||
else
|
else
|
||||||
res = clutter_binding_pool_activate (pool, event->keyval,
|
res = clutter_binding_pool_activate (pool, event->keyval,
|
||||||
@ -2105,6 +2241,9 @@ clutter_text_key_press (ClutterActor *actor,
|
|||||||
{
|
{
|
||||||
gunichar key_unichar;
|
gunichar key_unichar;
|
||||||
|
|
||||||
|
if (clutter_input_focus_filter_key_event (priv->input_focus, event))
|
||||||
|
return CLUTTER_EVENT_STOP;
|
||||||
|
|
||||||
/* Skip keys when control is pressed */
|
/* Skip keys when control is pressed */
|
||||||
key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event);
|
key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event);
|
||||||
|
|
||||||
@ -2141,6 +2280,19 @@ clutter_text_key_press (ClutterActor *actor,
|
|||||||
return CLUTTER_EVENT_PROPAGATE;
|
return CLUTTER_EVENT_PROPAGATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
clutter_text_key_release (ClutterActor *actor,
|
||||||
|
ClutterKeyEvent *event)
|
||||||
|
{
|
||||||
|
ClutterText *self = CLUTTER_TEXT (actor);
|
||||||
|
ClutterTextPrivate *priv = self->priv;
|
||||||
|
|
||||||
|
if (clutter_input_focus_filter_key_event (priv->input_focus, event))
|
||||||
|
return CLUTTER_EVENT_STOP;
|
||||||
|
|
||||||
|
return CLUTTER_EVENT_PROPAGATE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clutter_text_compute_layout_offsets (ClutterText *self,
|
clutter_text_compute_layout_offsets (ClutterText *self,
|
||||||
PangoLayout *layout,
|
PangoLayout *layout,
|
||||||
@ -2663,6 +2815,11 @@ static void
|
|||||||
clutter_text_key_focus_in (ClutterActor *actor)
|
clutter_text_key_focus_in (ClutterActor *actor)
|
||||||
{
|
{
|
||||||
ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
|
ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
|
||||||
|
ClutterBackend *backend = clutter_get_default_backend ();
|
||||||
|
ClutterInputMethod *method = clutter_backend_get_input_method (backend);
|
||||||
|
|
||||||
|
if (method && priv->editable)
|
||||||
|
clutter_input_method_focus_in (method, priv->input_focus);
|
||||||
|
|
||||||
priv->has_focus = TRUE;
|
priv->has_focus = TRUE;
|
||||||
|
|
||||||
@ -2673,9 +2830,14 @@ static void
|
|||||||
clutter_text_key_focus_out (ClutterActor *actor)
|
clutter_text_key_focus_out (ClutterActor *actor)
|
||||||
{
|
{
|
||||||
ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
|
ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
|
||||||
|
ClutterBackend *backend = clutter_get_default_backend ();
|
||||||
|
ClutterInputMethod *method = clutter_backend_get_input_method (backend);
|
||||||
|
|
||||||
priv->has_focus = FALSE;
|
priv->has_focus = FALSE;
|
||||||
|
|
||||||
|
if (priv->editable && clutter_input_focus_is_focused (priv->input_focus))
|
||||||
|
clutter_input_method_focus_out (method);
|
||||||
|
|
||||||
clutter_text_queue_redraw (actor);
|
clutter_text_queue_redraw (actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3369,6 +3531,7 @@ clutter_text_class_init (ClutterTextClass *klass)
|
|||||||
actor_class->get_preferred_height = clutter_text_get_preferred_height;
|
actor_class->get_preferred_height = clutter_text_get_preferred_height;
|
||||||
actor_class->allocate = clutter_text_allocate;
|
actor_class->allocate = clutter_text_allocate;
|
||||||
actor_class->key_press_event = clutter_text_key_press;
|
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_press_event = clutter_text_button_press;
|
||||||
actor_class->button_release_event = clutter_text_button_release;
|
actor_class->button_release_event = clutter_text_button_release;
|
||||||
actor_class->motion_event = clutter_text_motion;
|
actor_class->motion_event = clutter_text_motion;
|
||||||
@ -4169,6 +4332,8 @@ clutter_text_init (ClutterText *self)
|
|||||||
g_signal_connect (self, "notify::text-direction",
|
g_signal_connect (self, "notify::text-direction",
|
||||||
G_CALLBACK (clutter_text_direction_changed_cb),
|
G_CALLBACK (clutter_text_direction_changed_cb),
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
priv->input_focus = clutter_text_input_focus_new (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4459,6 +4624,8 @@ void
|
|||||||
clutter_text_set_editable (ClutterText *self,
|
clutter_text_set_editable (ClutterText *self,
|
||||||
gboolean editable)
|
gboolean editable)
|
||||||
{
|
{
|
||||||
|
ClutterBackend *backend = clutter_get_default_backend ();
|
||||||
|
ClutterInputMethod *method = clutter_backend_get_input_method (backend);
|
||||||
ClutterTextPrivate *priv;
|
ClutterTextPrivate *priv;
|
||||||
|
|
||||||
g_return_if_fail (CLUTTER_IS_TEXT (self));
|
g_return_if_fail (CLUTTER_IS_TEXT (self));
|
||||||
@ -4469,6 +4636,14 @@ clutter_text_set_editable (ClutterText *self,
|
|||||||
{
|
{
|
||||||
priv->editable = editable;
|
priv->editable = editable;
|
||||||
|
|
||||||
|
if (method)
|
||||||
|
{
|
||||||
|
if (!priv->editable && clutter_input_focus_is_focused (priv->input_focus))
|
||||||
|
clutter_input_method_focus_out (method);
|
||||||
|
else if (priv->has_focus)
|
||||||
|
clutter_input_method_focus_in (method, priv->input_focus);
|
||||||
|
}
|
||||||
|
|
||||||
clutter_text_queue_redraw (CLUTTER_ACTOR (self));
|
clutter_text_queue_redraw (CLUTTER_ACTOR (self));
|
||||||
|
|
||||||
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]);
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]);
|
||||||
|
Loading…
Reference in New Issue
Block a user