From a98720ae19f4892676c043bb6e4f2258eb201827 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:12:48 +0000 Subject: [PATCH 001/108] Initial import of the Text actor from Tidy The TidyText actor is meant as a replacement for both ClutterLabel and ClutterText. Any text-displaying and editing actor should derive from ClutterText and implement the various visual cues to differentiate the editable from the non-editable state. Those visual cues usually belong to a high-level toolkit, especially if themeing is involved. --- clutter/Makefile.am | 2 + clutter/clutter-text.c | 1561 +++++++++++++++++++++++++++++++++ clutter/clutter-text.h | 136 +++ clutter/clutter.h | 1 + tests/interactive/Makefile.am | 3 +- tests/interactive/test-text.c | 176 ++++ 6 files changed, 1878 insertions(+), 1 deletion(-) create mode 100644 clutter/clutter-text.c create mode 100644 clutter/clutter-text.h create mode 100644 tests/interactive/test-text.c diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 97c9907ad..f51523200 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -86,6 +86,7 @@ source_h = \ $(srcdir)/clutter-stage.h \ $(srcdir)/clutter-stage-manager.h \ $(srcdir)/clutter-texture.h \ + $(srcdir)/clutter-text.h \ $(srcdir)/clutter-timeline.h \ $(srcdir)/clutter-timeout-pool.h \ $(srcdir)/clutter-types.h \ @@ -180,6 +181,7 @@ source_c = \ clutter-stage-manager.c \ clutter-stage-window.c \ clutter-texture.c \ + clutter-text.c \ clutter-timeline.c \ clutter-timeout-pool.c \ clutter-units.c \ diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c new file mode 100644 index 000000000..6733fe1bd --- /dev/null +++ b/clutter/clutter-text.c @@ -0,0 +1,1561 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2006-2008 OpenedHand + * + * Authored By Øyvind Kolås + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* TODO: undo/redo hooks? + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "clutter-text.h" +#include + +static gboolean clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *kev); +static gboolean clutter_text_position_to_coords (ClutterText *ttext, + gint position, + gint *x, + gint *y, + gint *cursor_height); +static gint clutter_text_coords_to_position (ClutterText *text, + gint x, + gint y); +static void clutter_text_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void clutter_text_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void clutter_text_finalize (GObject *gobject); + +static void init_commands (ClutterText *ttext); +static void init_mappings (ClutterText *ttext); + +void +clutter_text_delete_text (ClutterText *ttext, + gssize start_pos, + gssize end_pos); + +static gboolean +clutter_text_truncate_selection (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); + +G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_LABEL); + +struct _ClutterTextPrivate +{ + gboolean editable; + gboolean cursor_visible; + gboolean activatable; + gboolean selectable; + + gint position; /* current cursor position */ + gint selection_bound; + /* current 'other end of selection' position */ + gboolean in_select_drag; + gint x_pos; /* the x position in the pangolayout, used to + * avoid drifting when repeatedly moving up|down + */ + gboolean cursor_color_set; + ClutterColor cursor_color; + ClutterGeometry cursor_pos; /* Where to draw the cursor */ + + GList *mappings; + GList *commands; /* each instance has it's own set of commands + so that actor specific actions can be added + to single actor classes + */ +}; + +#define CLUTTER_TEXT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + CLUTTER_TYPE_TEXT, \ + ClutterTextPrivate)) + +enum +{ + PROP_0, + PROP_POSITION, + PROP_SELECTION_BOUND, + PROP_CURSOR_VISIBLE, + PROP_CURSOR_COLOR, + PROP_CURSOR_COLOR_SET, + PROP_EDITABLE, + PROP_SELECTABLE, + PROP_ACTIVATABLE +}; + +enum +{ + TEXT_CHANGED, + CURSOR_EVENT, + ACTIVATE, + LAST_SIGNAL +}; + +static guint label_signals[LAST_SIGNAL] = { 0, }; + +#define offset_real(text, pos) \ + (pos==-1?g_utf8_strlen(text, -1):pos) \ + +#define offset_to_bytes(text,pos)\ + (pos==-1?strlen(text):((gint)(g_utf8_offset_to_pointer (text, pos) - text))) + +#define bytes_to_offset(text, pos) \ + (g_utf8_pointer_to_offset (text, text + pos)) + + +typedef struct TextCommand { + const gchar *name; + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); +} TextCommand; + +typedef struct ClutterTextMapping { + ClutterModifierType state; + guint keyval; + const gchar *action; +} ClutterTextMapping; + + +void +clutter_text_mappings_clear (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + GList *iter; + for (iter = priv->mappings; iter; iter=iter->next) + { + g_free (iter->data); + } + g_list_free (priv->mappings); + priv->mappings = NULL; +} + +void clutter_text_add_mapping (ClutterText *ttext, + guint keyval, + ClutterModifierType state, + const gchar *commandline) +{ + ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); + ClutterTextPrivate *priv = ttext->priv; + tmapping->keyval = keyval; + tmapping->state = state; + tmapping->action = commandline; + priv->mappings = g_list_append (priv->mappings, tmapping); +} + +void clutter_text_add_action (ClutterText *ttext, + const gchar *name, + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event)) +{ + TextCommand *tcommand = g_new (TextCommand, 1); + ClutterTextPrivate *priv = ttext->priv; + tcommand->name = name; + tcommand->func = func; + priv->commands = g_list_append (priv->commands, tcommand); +} + +static void init_mappings (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + if (priv->mappings) + return; + clutter_text_add_mapping (ttext, CLUTTER_Left, 0, "move-left"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Left, 0, "move-left"); + clutter_text_add_mapping (ttext, CLUTTER_Right, 0, "move-right"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Right, 0,"move-right"); + clutter_text_add_mapping (ttext, CLUTTER_Up, 0, "move-up"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Up, 0, "move-up"); + clutter_text_add_mapping (ttext, CLUTTER_Down, 0, "move-down"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Down, 0, "move-down"); + clutter_text_add_mapping (ttext, CLUTTER_Begin, 0, "move-start-line"); + clutter_text_add_mapping (ttext, CLUTTER_Home, 0, "move-start-line"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Home, 0, "move-start-line"); + clutter_text_add_mapping (ttext, CLUTTER_End, 0, "move-end-line"); + clutter_text_add_mapping (ttext, CLUTTER_KP_End, 0, "move-end-line"); + clutter_text_add_mapping (ttext, CLUTTER_BackSpace,0,"delete-previous"); + clutter_text_add_mapping (ttext, CLUTTER_Delete, 0, "delete-next"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0,"delete-next"); + clutter_text_add_mapping (ttext, CLUTTER_Return, 0, "activate"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0,"activate"); + clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter,0,"activate"); +} + + +static gint +clutter_text_coords_to_position (ClutterText *text, + gint x, + gint y) +{ + gint index_; + gint px, py; + gint trailing; + + px = x * PANGO_SCALE; + py = y * PANGO_SCALE; + + pango_layout_xy_to_index (clutter_label_get_layout (CLUTTER_LABEL (text)), + px, py, &index_, &trailing); + + return index_ + trailing; +} + +static gboolean +clutter_text_position_to_coords (ClutterText *ttext, + gint position, + gint *x, + gint *y, + gint *cursor_height) +{ + ClutterTextPrivate *priv; + gint index_; + PangoRectangle rect; + const gchar *text; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + priv = ttext->priv; + + if (position == -1) + { + index_ = strlen (text); + } + else + { + index_ = offset_to_bytes (text, position); + } + + if (index_ > strlen (text)) + index_ = strlen (text); + + pango_layout_get_cursor_pos ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + index_, &rect, NULL); + + if (x) + *x = rect.x / PANGO_SCALE; + if (y) + *y = (rect.y + rect.height) / PANGO_SCALE; + if (cursor_height) + *cursor_height = rect.height / PANGO_SCALE; + + return TRUE; /* FIXME: should return false if coords were outside text */ +} + +static void +clutter_text_ensure_cursor_position (ClutterText *ttext) +{ + gint x,y,cursor_height; + + ClutterTextPrivate *priv; + priv = ttext->priv; + + clutter_text_position_to_coords (ttext, priv->position, &x, &y, &cursor_height); + + priv->cursor_pos.x = x; + priv->cursor_pos.y = y - cursor_height; + priv->cursor_pos.width = 2; + priv->cursor_pos.height = cursor_height; + + g_signal_emit (ttext, label_signals[CURSOR_EVENT], 0, &priv->cursor_pos); +} + +gint +clutter_text_get_cursor_position (ClutterText *ttext) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (ttext), -1); + return ttext->priv->position; +} + +void +clutter_text_set_cursor_position (ClutterText *ttext, + gint position) +{ + ClutterLabel *label = CLUTTER_LABEL (ttext); + const gchar *text; + ClutterTextPrivate *priv; + gint len; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + + priv = ttext->priv; + + text = clutter_label_get_text (label); + if (text == NULL) + return; + + len = g_utf8_strlen (text, -1); + + if (position < 0 || position >= len) + priv->position = -1; + else + priv->position = position; + + if (CLUTTER_ACTOR_IS_VISIBLE (ttext)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (ttext)); +} + +static gboolean +clutter_text_truncate_selection (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + ClutterTextPrivate *priv; + gint start_index; + gint end_index; + + priv = ttext->priv; + + g_object_ref (ttext); + + start_index = offset_real (utf8, priv->position); + end_index = offset_real (utf8, priv->selection_bound); + + if (end_index == start_index) + return FALSE; + + if (end_index < start_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + clutter_text_delete_text (ttext, start_index, end_index); + priv->position = start_index; + priv->selection_bound = start_index; + return TRUE; +} + + +void +clutter_text_insert_unichar (ClutterText *ttext, + gunichar wc) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + const gchar *old_text; + glong pos; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + g_return_if_fail (g_unichar_validate (wc)); + + if (wc == 0) + return; + + clutter_text_truncate_selection (ttext, NULL, 0); + + priv = ttext->priv; + + g_object_ref (ttext); + + old_text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + + new = g_string_new (old_text); + pos = offset_to_bytes (old_text, priv->position); + new = g_string_insert_unichar (new, pos, wc); + + clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + + if (priv->position >= 0) + { + clutter_text_set_cursor_position (ttext, priv->position + 1); + clutter_text_set_selection_bound (ttext, priv->position); + } + + g_string_free (new, TRUE); + + g_object_unref (ttext); + + g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); +} + +void +clutter_text_delete_text (ClutterText *ttext, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint start_bytes; + gint end_bytes; + const gchar *text; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + + priv = ttext->priv; + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + if (end_pos == -1) + { + start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); + end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); + } + else + { + start_bytes = offset_to_bytes (text, start_pos); + end_bytes = offset_to_bytes (text, end_pos); + } + + new = g_string_new (text); + + new = g_string_erase (new, start_bytes, end_bytes - start_bytes); + + clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + + g_string_free (new, TRUE); + g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); +} + +static void +clutter_text_finalize (GObject *gobject) +{ + ClutterTextPrivate *priv; + ClutterText *ttext; + GList *iter; + + ttext = CLUTTER_TEXT (gobject); + priv = ttext->priv; + + clutter_text_mappings_clear (ttext); + + for (iter = priv->commands; iter; iter=iter->next) + g_free (iter->data); + g_list_free (priv->commands); + priv->commands = NULL; +} + +static void +clutter_text_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterTextPrivate *priv; + ClutterText *ttext; + + ttext = CLUTTER_TEXT (gobject); + priv = ttext->priv; + + switch (prop_id) + { + case PROP_POSITION: + clutter_text_set_cursor_position (ttext, g_value_get_int (value)); + break; + case PROP_SELECTION_BOUND: + clutter_text_set_selection_bound (ttext, g_value_get_int (value)); + break; + case PROP_CURSOR_VISIBLE: + clutter_text_set_cursor_visible (ttext, g_value_get_boolean (value)); + break; + case PROP_CURSOR_COLOR: + clutter_text_set_cursor_color (ttext, g_value_get_boxed (value)); + break; + case PROP_EDITABLE: + clutter_text_set_editable (ttext, g_value_get_boolean (value)); + break; + case PROP_ACTIVATABLE: + clutter_text_set_activatable (ttext, g_value_get_boolean (value)); + break; + case PROP_SELECTABLE: + clutter_text_set_selectable (ttext, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_text_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterTextPrivate *priv; + + priv = CLUTTER_TEXT (gobject)->priv; + + switch (prop_id) + { + case PROP_CURSOR_VISIBLE: + g_value_set_boolean (value, priv->cursor_visible); + break; + case PROP_CURSOR_COLOR: + { + ClutterColor color; + clutter_text_get_cursor_color (CLUTTER_TEXT (gobject), &color); + g_value_set_boxed (value, &color); + } + break; + case PROP_POSITION: + g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); + break; + case PROP_SELECTION_BOUND: + g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->selection_bound)); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, priv->editable); + break; + case PROP_SELECTABLE: + g_value_set_boolean (value, priv->selectable); + break; + case PROP_ACTIVATABLE: + g_value_set_boolean (value, priv->activatable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +cursor_paint (ClutterActor *actor, + gpointer user_data) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + + if (priv->editable && + priv->cursor_visible) + { + if (priv->cursor_color_set) + { + cogl_set_source_color4ub (priv->cursor_color.red, + priv->cursor_color.green, + priv->cursor_color.blue, + priv->cursor_color.alpha); + } + else + { + ClutterColor color; + clutter_label_get_color (CLUTTER_LABEL (actor), &color); + cogl_set_source_color4ub (color.red, + color.green, + color.blue, + color.alpha); + } + + clutter_text_ensure_cursor_position (ttext); + + if (priv->position == 0) + priv->cursor_pos.x -= 2; + + if (priv->position == priv->selection_bound) + { + cogl_rectangle (priv->cursor_pos.x, + priv->cursor_pos.y, + priv->cursor_pos.width, + priv->cursor_pos.height); + } + else + { + gint lines; + gint start_index; + gint end_index; + const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + start_index = offset_to_bytes (utf8, priv->position); + end_index = offset_to_bytes (utf8, priv->selection_bound); + + if (start_index > end_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + PangoLayout *layout = clutter_label_get_layout (CLUTTER_LABEL (ttext)); + lines = pango_layout_get_line_count (layout); + gint line_no; + for (line_no = 0; line_no < lines; line_no++) + { + PangoLayoutLine *line; + gint n_ranges; + gint *ranges; + gint i; + gint y; + gint height; + gint index; + gint maxindex; + + line = pango_layout_get_line_readonly (layout, line_no); + pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL); + if (maxindex < start_index) + continue; + + pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); + pango_layout_line_x_to_index (line, 0, &index, NULL); + + clutter_text_position_to_coords (ttext, bytes_to_offset (utf8, index), NULL, &y, &height); + + for (i=0;ipriv; + ClutterUnit x, y; + gint index_; + const gchar *text; + + text = clutter_label_get_text (CLUTTER_LABEL (actor)); + + x = CLUTTER_UNITS_FROM_INT (bev->x); + y = CLUTTER_UNITS_FROM_INT (bev->y); + + clutter_actor_transform_stage_point (actor, x, y, &x, &y); + + index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); + + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_) + ); + + /* we'll steal keyfocus if we do not have it */ + { + ClutterActor *stage; + for (stage = actor; + clutter_actor_get_parent (stage); + stage = clutter_actor_get_parent (stage)); + if (stage && CLUTTER_IS_STAGE (stage)) + clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); + } + + priv->in_select_drag = TRUE; + clutter_grab_pointer (actor); + + return TRUE; +} + + +static gboolean +clutter_text_motion (ClutterActor *actor, + ClutterMotionEvent *mev) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + ClutterUnit x, y; + gint index_; + const gchar *text; + + if (!priv->in_select_drag) + { + return FALSE; + } + + text = clutter_label_get_text (CLUTTER_LABEL (actor)); + + x = CLUTTER_UNITS_FROM_INT (mev->x); + y = CLUTTER_UNITS_FROM_INT (mev->y); + + clutter_actor_transform_stage_point (actor, x, y, &x, &y); + + index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); + + if (priv->selectable) + { + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + } + else + { + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); + clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_)); + } + + return TRUE; +} + +static gboolean +clutter_text_release (ClutterActor *actor, + ClutterButtonEvent *bev) +{ + ClutterText *ttext = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = ttext->priv; + if (priv->in_select_drag) + { + clutter_ungrab_pointer (); + priv->in_select_drag = FALSE; + return TRUE; + } + return FALSE; +} + +static void +clutter_text_constructed (GObject *object) +{ + g_signal_connect (CLUTTER_ACTOR (object), + "paint", G_CALLBACK (cursor_paint), + NULL); + + if (G_OBJECT_CLASS (clutter_text_parent_class)->constructed != NULL) + G_OBJECT_CLASS (clutter_text_parent_class)->constructed (object); +} + +static void +clutter_text_class_init (ClutterTextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->set_property = clutter_text_set_property; + gobject_class->get_property = clutter_text_get_property; + gobject_class->constructed = clutter_text_constructed; + gobject_class->finalize = clutter_text_finalize; + + actor_class->key_press_event = clutter_text_key_press; + actor_class->button_press_event = clutter_text_press; + actor_class->button_release_event = clutter_text_release; + actor_class->motion_event = clutter_text_motion; + + /** + * ClutterText:editable: + * + * Whether key events delivered to the actor causes editing. + */ + g_object_class_install_property + (gobject_class, PROP_EDITABLE, + g_param_spec_boolean ("editable", + "Editable", + "Whether the text is editable", + TRUE, + G_PARAM_READWRITE)); + + + /** + * ClutterText:selectable: + * + * Whether it is possible to select text. + */ + g_object_class_install_property + (gobject_class, PROP_SELECTABLE, + g_param_spec_boolean ("selectable", + "Editable", + "Whether the text is selectable", + TRUE, + G_PARAM_READWRITE)); + + /** + * ClutterText:activatable: + * + * Toggles whether return invokes the activate signal or not. + */ + g_object_class_install_property + (gobject_class, PROP_ACTIVATABLE, + g_param_spec_boolean ("activatable", + "Editable", + "Whether return causes the activate signal to be fired", + TRUE, + G_PARAM_READWRITE)); + + /** + * ClutterText:cursor-visible: + * + * Whether the input cursor is visible or not, it will only be visible + * if both cursor-visible is set and editable is set at the same time, + * the value defaults to TRUE. + */ + g_object_class_install_property + (gobject_class, PROP_CURSOR_VISIBLE, + g_param_spec_boolean ("cursor-visible", + "Cursor Visible", + "Whether the input cursor is visible", + TRUE, + G_PARAM_READWRITE)); + + + g_object_class_install_property + (gobject_class, PROP_CURSOR_COLOR, + g_param_spec_boxed ("cursor-color", + "Cursor Colour", + "Cursor Colour", + CLUTTER_TYPE_COLOR, + G_PARAM_READWRITE)); + + /** + * ClutterText:position: + * + * The current input cursor position. -1 is taken to be the end of the text + */ + g_object_class_install_property + (gobject_class, PROP_POSITION, + g_param_spec_int ("position", + "Position", + "The cursor position", + -1, G_MAXINT, + -1, + G_PARAM_READWRITE)); + + + /** + * ClutterText:selection-bound: + * + * The current input cursor position. -1 is taken to be the end of the text + */ + g_object_class_install_property + (gobject_class, PROP_SELECTION_BOUND, + g_param_spec_int ("selection-bound", + "Selection-bound", + "The cursor position of the other end of the selection.", + -1, G_MAXINT, + -1, + G_PARAM_READWRITE)); + + /** + * ClutterText::text-changed: + * @actor: the actor which received the event + * + * The ::text-changed signal is emitted after @entry's text changes + */ + label_signals[TEXT_CHANGED] = + g_signal_new ("text-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterEntryClass, text_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + + label_signals[CURSOR_EVENT] = + g_signal_new ("cursor-event", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); + + + /** + * ClutterText::activate + * @actor: the actor which received the event + * + * The ::activate signal is emitted each time the entry is 'activated' + * by the user, normally by pressing the 'Enter' key. + * + * Since: 0.4 + */ + label_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterEntryClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + + g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); +} + +static void +clutter_text_init (ClutterText *self) +{ + ClutterTextPrivate *priv; + + self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); + priv->x_pos = -1; + priv->cursor_visible = TRUE; + priv->editable = FALSE; + + priv->cursor_color_set = FALSE; + init_commands (self); /* FIXME: free */ + init_mappings (self); /* FIXME: free */ +} + +ClutterActor * +clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + "color", color, + NULL); +} + +ClutterActor * +clutter_text_new_with_text (const gchar *font_name, + const gchar *text) +{ + return g_object_new (CLUTTER_TYPE_TEXT, + "font-name", font_name, + "text", text, + NULL); +} + + +void +clutter_text_set_editable (ClutterText *label, + gboolean editable) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->editable = editable; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_editable (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->editable; +} + + +void +clutter_text_set_selectable (ClutterText *label, + gboolean selectable) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->selectable = selectable; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_selectable (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->selectable; +} + + +void +clutter_text_set_activatable (ClutterText *label, + gboolean activatable) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->activatable = activatable; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_activatable (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->activatable; +} + + +void +clutter_text_set_cursor_visible (ClutterText *label, + gboolean cursor_visible) +{ + ClutterTextPrivate *priv; + + priv = label->priv; + priv->cursor_visible = cursor_visible; + clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); +} + +gboolean +clutter_text_get_cursor_visible (ClutterText *label) +{ + ClutterTextPrivate *priv; + priv = label->priv; + return priv->cursor_visible; +} + +void +clutter_text_set_cursor_color (ClutterText *text, + const ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + g_object_ref (text); + + if (color) + { + priv->cursor_color = *color; + priv->cursor_color_set = TRUE; + } + else + { + priv->cursor_color_set = FALSE; + } +} + + +void +clutter_text_get_cursor_color (ClutterText *text, + ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + *color = priv->cursor_color; +} + + +gint +clutter_text_get_selection_bound (ClutterText *text) +{ + ClutterTextPrivate *priv; + + priv = text->priv; + + return priv->selection_bound; +} + +gchar * +clutter_text_get_selection (ClutterText *text) +{ + ClutterTextPrivate *priv; + + const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (text)); + gchar *str; + gint len; + gint start_index; + gint end_index; + gint start_offset; + gint end_offset; + + priv = text->priv; + + start_index = priv->position; + end_index = priv->selection_bound; + + if (end_index == start_index) + return g_strdup (""); + if (end_index < start_index) + { + gint temp = start_index; + start_index = end_index; + end_index = temp; + } + + start_offset = offset_to_bytes (utf8, start_index); + end_offset = offset_to_bytes (utf8, end_index); + len = end_offset - start_offset; + + str = g_malloc (len + 1); + g_utf8_strncpy (str, utf8 + start_offset, end_index-start_index); + return str; +} + + + +void +clutter_text_set_selection_bound (ClutterText *text, + gint selection_bound) +{ + ClutterTextPrivate *priv; + + priv = text->priv; + priv->selection_bound = selection_bound; + + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); +} + + +/****************************************************************/ +/* The following are the commands available for keybinding when */ +/* using the entry, these can also be invoked programmatically */ +/* through clutter_text_action() */ +/****************************************************************/ + +static gboolean +clutter_text_action_activate (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + g_signal_emit (G_OBJECT (ttext), label_signals[ACTIVATE], 0); + return TRUE; +} + +static void +clutter_text_clear_selection (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + priv->selection_bound = priv->position; +} + +static gboolean +clutter_text_action_move_left (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint pos = priv->position; + gint len; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + if (pos != 0 && len !=0) + { + if (pos == -1) + { + clutter_text_set_cursor_position (ttext, len - 1); + } + else + { + clutter_text_set_cursor_position (ttext, pos - 1); + } + } + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + + +static gboolean +clutter_text_action_move_right (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint pos; + + gint len; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + pos = priv->position; + if (pos != -1 && len !=0) + { + if (pos != len) + { + clutter_text_set_cursor_position (ttext, pos + 1); + } + } + + if (!(priv->selectable && + event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + { + clutter_text_clear_selection (ttext); + } + + return TRUE; +} + +static gboolean +clutter_text_action_move_up (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + gint x; + const gchar *text; + PangoLayoutLine *layout_line; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + offset_to_bytes (text, priv->position), + 0, + &line_no, + &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + line_no -= 1; + if (line_no < 0) + return FALSE; + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no); + + if (!layout_line) + return TRUE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (text, index_); + clutter_text_set_cursor_position (ttext, pos); + } + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_move_down (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + gint x; + const gchar *text; + PangoLayoutLine *layout_line; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + offset_to_bytes (text, priv->position), + 0, + &line_no, + &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no + 1); + + if (!layout_line) + { + return FALSE; + } + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (text, index_); + clutter_text_set_cursor_position (ttext, pos); + } + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + return TRUE; +} + +static gboolean +clutter_text_action_move_start (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + + clutter_text_set_cursor_position (ttext, 0); + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + return TRUE; +} + +static gboolean +clutter_text_action_move_end (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + + clutter_text_set_cursor_position (ttext, -1); + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_move_start_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + const gchar *text; + PangoLayoutLine *layout_line; + gint position; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + offset_to_bytes (text, priv->position), + 0, + &line_no, + NULL); + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no); + + pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); + + position = bytes_to_offset (text, index_); + clutter_text_set_cursor_position (ttext, position); + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_move_end_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv = ttext->priv; + gint line_no; + gint index_; + gint trailing; + const gchar *text; + PangoLayoutLine *layout_line; + gint position; + + text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + + index_ = offset_to_bytes (text, priv->position); + + pango_layout_index_to_line_x ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + index_, + 0, + &line_no, + NULL); + + layout_line = pango_layout_get_line_readonly ( + clutter_label_get_layout (CLUTTER_LABEL (ttext)), + line_no); + + pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); + index_ += trailing; + + position = bytes_to_offset (text, index_); + + clutter_text_set_cursor_position (ttext, position); + + if (!(priv->selectable && event && + (event->key.modifier_state & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (ttext); + + return TRUE; +} + +static gboolean +clutter_text_action_delete_next (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (ttext, NULL, 0)) + return TRUE; + priv = ttext->priv; + pos = priv->position; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + if (len && pos != -1 && pos < len) + clutter_text_delete_text (ttext, pos, pos+1);; + return TRUE; +} + +static gboolean +clutter_text_action_delete_previous (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + ClutterTextPrivate *priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (ttext, NULL, 0)) + return TRUE; + priv = ttext->priv; + pos = priv->position; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + if (pos != 0 && len !=0) + { + if (pos == -1) + { + clutter_text_set_cursor_position (ttext, len - 1); + clutter_text_set_selection_bound (ttext, len - 1); + } + else + { + clutter_text_set_cursor_position (ttext, pos - 1); + clutter_text_set_selection_bound (ttext, pos - 1); + } + clutter_text_delete_text (ttext, pos-1, pos);; + } + return TRUE; +} + + +static void init_commands (ClutterText *ttext) +{ + ClutterTextPrivate *priv = ttext->priv; + if (priv->commands) + return; + clutter_text_add_action (ttext, "move-left", clutter_text_action_move_left); + clutter_text_add_action (ttext, "move-right", clutter_text_action_move_right); + clutter_text_add_action (ttext, "move-up", clutter_text_action_move_up); + clutter_text_add_action (ttext, "move-down", clutter_text_action_move_down); + clutter_text_add_action (ttext, "move-start", clutter_text_action_move_start); + clutter_text_add_action (ttext, "move-end", clutter_text_action_move_end); + clutter_text_add_action (ttext, "move-start-line", clutter_text_action_move_start_line); + clutter_text_add_action (ttext, "move-end-line", clutter_text_action_move_end_line); + clutter_text_add_action (ttext, "delete-previous", clutter_text_action_delete_previous); + clutter_text_add_action (ttext, "delete-next", clutter_text_action_delete_next); + clutter_text_add_action (ttext, "activate", clutter_text_action_activate); + clutter_text_add_action (ttext, "truncate-selection", clutter_text_truncate_selection); +} + +gboolean +clutter_text_action (ClutterText *ttext, + const gchar *command, + ClutterEvent *event) +{ + gchar command2[64]; + gint i; + + GList *iter; + ClutterTextPrivate *priv = ttext->priv; + + for (i=0; command[i] && + command[i]!=' '&& + i<62; i++) + { + command2[i]=command[i]; + } + command2[i]='\0'; + + if (!g_str_equal (command2, "move-up") && + !g_str_equal (command2, "move-down")) + priv->x_pos = -1; + + for (iter=priv->commands;iter;iter=iter->next) + { + TextCommand *tcommand = iter->data; + if (g_str_equal (command2, tcommand->name)) + return tcommand->func (ttext, command, event); + } + + g_warning ("unhandled text command %s", command); + return FALSE; +} + +static gboolean +clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *kev) +{ + ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + gint keyval = clutter_key_event_symbol (kev); + GList *iter; + + if (!priv->editable) + return FALSE; + + for (iter=priv->mappings;iter;iter=iter->next) + { + ClutterTextMapping *mapping = iter->data; + + if ( + (mapping->keyval == keyval) && + ( + (mapping->state == 0) || + (mapping->state && (kev->modifier_state & mapping->state)) + ) + ) + { + if (!g_str_equal (mapping->action, "activate") || + priv->activatable) + return clutter_text_action (CLUTTER_TEXT (actor), mapping->action, (ClutterEvent*)kev); + } + } + + { + gunichar key_unichar = clutter_key_event_unicode (kev); + + if (key_unichar == '\r') /* return is reported as CR we want LF */ + key_unichar = '\n'; + if (g_unichar_validate (key_unichar)) + { + clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + return TRUE; + } + } + return FALSE; +} + diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h new file mode 100644 index 000000000..908275cc2 --- /dev/null +++ b/clutter/clutter-text.h @@ -0,0 +1,136 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2006-2008 OpenedHand + * + * Authored By Øyvind Kolås + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CLUTTER_TEXT_H__ +#define __CLUTTER_TEXT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) + +#define CLUTTER_TEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + CLUTTER_TYPE_TEXT, ClutterText)) + +#define CLUTTER_TEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + CLUTTER_TYPE_TEXT, ClutterTextClass)) + +#define CLUTTER_IS_TEXT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + CLUTTER_TYPE_TEXT)) + +#define CLUTTER_IS_TEXT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + CLUTTER_TYPE_TEXT)) + +#define CLUTTER_TEXT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + CLUTTER_TYPE_TEXT, ClutterTextClass)) + +typedef struct _ClutterText ClutterText; +typedef struct _ClutterTextPrivate ClutterTextPrivate; +typedef struct _ClutterTextClass ClutterTextClass; + +struct _ClutterText +{ + ClutterLabel parent_instance; + + /*< private >*/ + ClutterTextPrivate *priv; +}; + +struct _ClutterTextClass +{ + ClutterLabelClass parent_class; + + void (* text_changed) (ClutterText *text); + void (* activate) (ClutterText *text); + void (* cursor_event) (ClutterText *text, + ClutterGeometry *geometry); +}; + +GType clutter_text_get_type (void) G_GNUC_CONST; + +ClutterActor *clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color); +ClutterActor *clutter_text_new_with_text (const gchar *font_name, + const gchar *text); + +void clutter_text_set_editable (ClutterText *label, + gboolean editable); +gboolean clutter_text_get_editable (ClutterText *label); +void clutter_text_set_activatable (ClutterText *label, + gboolean activatable); +gboolean clutter_text_get_activatable (ClutterText *label); + +gint clutter_text_get_cursor_position (ClutterText *label); +void clutter_text_set_cursor_position (ClutterText *label, + gint position); +void clutter_text_set_cursor_visible (ClutterText *label, + gboolean cursor_visible); +gboolean clutter_text_get_cursor_visible (ClutterText *label); +void clutter_text_set_cursor_color (ClutterText *text, + const ClutterColor *color); +void clutter_text_get_cursor_color (ClutterText *text, + ClutterColor *color); +void clutter_text_set_selectable (ClutterText *label, + gboolean selectable); +gboolean clutter_text_get_selectable (ClutterText *label); +void clutter_text_set_selection_bound (ClutterText *text, + gint selection_bound); +gint clutter_text_get_selection_bound (ClutterText *text); +gchar * clutter_text_get_selection (ClutterText *text); +void clutter_text_insert_unichar (ClutterText *ttext, + gunichar wc); + + +/* add a custom action that can be used in keybindings */ +void clutter_text_add_action (ClutterText *ttext, + const gchar *name, + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event)); + +/* invoke an action registered by you or one of the tidy text default actions */ +gboolean clutter_text_action (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); + +void clutter_text_mappings_clear (ClutterText *ttext); + +/* Add a keybinding to handle for the default keypress vfunc handler */ +void clutter_text_add_mapping (ClutterText *ttext, + guint keyval, + ClutterModifierType state, + const gchar *commandline); + +G_END_DECLS + +#endif /* __CLUTTER_TEXT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 56c1e1789..664c88970 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -68,6 +68,7 @@ #include "clutter-stage.h" #include "clutter-stage-manager.h" #include "clutter-texture.h" +#include "clutter-text.h" #include "clutter-timeline.h" #include "clutter-timeout-pool.h" #include "clutter-types.h" diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index dcc4e673a..3a536162d 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -38,7 +38,8 @@ UNIT_TESTS = \ test-layout.c \ test-animation.c \ test-easing.c \ - test-binding-pool.c + test-binding-pool.c \ + test-text.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c new file mode 100644 index 000000000..6a0393f44 --- /dev/null +++ b/tests/interactive/test-text.c @@ -0,0 +1,176 @@ +/* Try this text editor, it has issues but does work, + * try ctrl+A, ctrl+C, ctrl+V, ctrl+X as well as selecting text with + * mouse and keyboard, /Øyvind K + */ + +#include +#include + +#define FONT "Mono Bold 22px" + +static gchar *clipboard = NULL; + +static gchar *runes = +"ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n" +"ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n" +"ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n"; + + +static gboolean +select_all (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + gint len; + len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + + clutter_text_set_cursor_position (ttext, 0); + clutter_text_set_selection_bound (ttext, len); + + return TRUE; +} + +static gboolean +copy (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + if (clipboard) + g_free (clipboard); + clipboard = clutter_text_get_selection (ttext); + return TRUE; +} + +static gboolean +paste (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + const gchar *p; + if (!clipboard) + return TRUE; + + for (p=clipboard; *p!='\0'; p=g_utf8_next_char (p)) + { + clutter_text_insert_unichar (ttext, g_utf8_get_char_validated (p, 3)); + } + return TRUE; +} + +static gboolean +cut (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + clutter_text_action (ttext, "copy", NULL); + clutter_text_action (ttext, "truncate-selection", NULL); + return TRUE; +} + +static gboolean +pageup (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + gint i; + for (i=0;i<10;i++) + clutter_text_action (ttext, "move-up", event); + return TRUE; +} + +static gboolean +pagedown (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) +{ + gint i; + for (i=0;i<10;i++) + clutter_text_action (ttext, "move-down", event); + return TRUE; +} + +static void cursor_event (ClutterText *text, + ClutterGeometry *geometry) +{ + gint y; + + y = clutter_actor_get_y (CLUTTER_ACTOR (text)); + + if (y + geometry->y < 50) + { + clutter_actor_set_y (CLUTTER_ACTOR (text), y + 100); + } + else if (y + geometry->y > 720) + { + clutter_actor_set_y (CLUTTER_ACTOR (text), y - 100); + } + +} + +G_MODULE_EXPORT gint +test_text_main (gint argc, + gchar **argv) +{ + ClutterActor *stage; + ClutterActor *text; + ClutterColor text_color = {0x33, 0xff, 0x33, 0xff}; + ClutterColor cursor_color = {0xff, 0x33, 0x33, 0xff}; + ClutterColor background_color = {0x00, 0x00, 0x00, 0xff}; + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color); + + text = clutter_text_new_full (FONT, "·", &text_color); + + clutter_container_add (CLUTTER_CONTAINER (stage), text, NULL); + clutter_actor_set_position (text, 40, 30); + clutter_actor_set_width (text, 1024); + clutter_label_set_line_wrap (CLUTTER_LABEL (text), TRUE); + + clutter_actor_set_reactive (text, TRUE); + clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text); + + clutter_text_set_editable (CLUTTER_TEXT (text), TRUE); + clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE); + clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color); + + clutter_text_add_action (CLUTTER_TEXT (text), "select-all", select_all); + clutter_text_add_action (CLUTTER_TEXT (text), "copy", copy); + clutter_text_add_action (CLUTTER_TEXT (text), "paste", paste); + clutter_text_add_action (CLUTTER_TEXT (text), "cut", cut); + clutter_text_add_action (CLUTTER_TEXT (text), "pageup", pageup); + clutter_text_add_action (CLUTTER_TEXT (text), "pagedown", pagedown); + + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_a, CLUTTER_CONTROL_MASK, "select-all"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_c, CLUTTER_CONTROL_MASK, "copy"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_v, CLUTTER_CONTROL_MASK, "paste"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_x, CLUTTER_CONTROL_MASK, "cut"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_Page_Up, 0, "pageup"); + clutter_text_add_mapping (CLUTTER_TEXT (text), + CLUTTER_Page_Down, 0, "pagedown"); + + if (argv[1]) + { + gchar *utf8; + g_file_get_contents (argv[1], &utf8, NULL, NULL); + clutter_label_set_text (CLUTTER_LABEL (text), utf8); + } + else + { + clutter_label_set_text (CLUTTER_LABEL (text), runes); + } + + g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL); + + clutter_actor_set_size (stage, 1024, 768); + clutter_actor_show (stage); + + clutter_main (); + return 0; +} From f9bf187e5d0268886a96a40911debd21b666e5e0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:15:11 +0000 Subject: [PATCH 002/108] Use internal headers Since ClutterText is part of Clutter, it can use the internal and private headers instead of the catch-all clutter.h. --- clutter/clutter-text.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 6733fe1bd..da0612934 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -33,7 +33,22 @@ #include #include "clutter-text.h" -#include + +#include "clutter-main.h" +#include "clutter-enum-types.h" +#include "clutter-private.h" +#include "clutter-debug.h" +#include "clutter-units.h" + +#include "cogl-pango.h" + +#define DEFAULT_FONT_NAME "Sans 10" + +/* Probably move into main */ +static PangoContext *_context = NULL; + +static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; +static const ClutterColor default_text_color = { 0, 0, 0, 255 }; static gboolean clutter_text_key_press (ClutterActor *actor, ClutterKeyEvent *kev); From a91bb5450f029200635d9fdaf470c27046ebdeae Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:15:56 +0000 Subject: [PATCH 003/108] Group all the integer private fields Integer fields using the ": " notation should be packed together so that the compiler can optimize the structure size correctly. --- clutter/clutter-text.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index da0612934..24e2e82ff 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -34,6 +34,7 @@ #include "clutter-text.h" +#include "clutter-keysyms.h" #include "clutter-main.h" #include "clutter-enum-types.h" #include "clutter-private.h" @@ -87,19 +88,19 @@ G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_LABEL); struct _ClutterTextPrivate { - gboolean editable; - gboolean cursor_visible; - gboolean activatable; - gboolean selectable; + guint editable : 1; + guint cursor_visible : 1; + guint activatable : 1; + guint selectable : 1; + guint in_select_drag : 1; + guint cursor_color_set : 1; gint position; /* current cursor position */ gint selection_bound; /* current 'other end of selection' position */ - gboolean in_select_drag; gint x_pos; /* the x position in the pangolayout, used to * avoid drifting when repeatedly moving up|down */ - gboolean cursor_color_set; ClutterColor cursor_color; ClutterGeometry cursor_pos; /* Where to draw the cursor */ @@ -879,7 +880,7 @@ clutter_text_class_init (ClutterTextClass *klass) g_signal_new ("text-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, text_changed), + G_STRUCT_OFFSET (ClutterTextClass, text_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -889,7 +890,7 @@ clutter_text_class_init (ClutterTextClass *klass) g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), + G_STRUCT_OFFSET (ClutterTextClass, cursor_event), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, @@ -909,7 +910,7 @@ clutter_text_class_init (ClutterTextClass *klass) g_signal_new ("activate", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, activate), + G_STRUCT_OFFSET (ClutterTextClass, activate), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); From 4d62da80adf7347d1be25cdc42255438e5d8fb64 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:17:26 +0000 Subject: [PATCH 004/108] Coalesce ClutterLabel API usage ClutterText replaces ClutterLabel, so it should expose the same kind of API - ideally with the minimal amount of changes, so that the porting is trivial. --- clutter/clutter-text.c | 693 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 608 insertions(+), 85 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 24e2e82ff..34ee928e0 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -45,6 +45,18 @@ #define DEFAULT_FONT_NAME "Sans 10" +/* We need at least three cached layouts to run the allocation without + regenerating a new layout. First the layout will be generated at + full width to get the preferred width, then it will be generated at + the preferred width to get the preferred height and then it might + be regenerated at a different width to get the height for the + actual allocated width */ +#define N_CACHED_LAYOUTS 3 + +#define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) + +typedef struct _LayoutCache LayoutCache; + /* Probably move into main */ static PangoContext *_context = NULL; @@ -71,6 +83,14 @@ static void clutter_text_get_property (GObject *gobject, GParamSpec *pspec); static void clutter_text_finalize (GObject *gobject); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); +void clutter_text_set_text (ClutterText *text, const gchar *str); +PangoLayout *clutter_text_get_layout (ClutterText *text); +void clutter_text_set_color (ClutterText *text, const ClutterColor *color); +void clutter_text_get_color (ClutterText *text, ClutterColor *color); +void clutter_text_set_font_name (ClutterText *text, const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); + static void init_commands (ClutterText *ttext); static void init_mappings (ClutterText *ttext); @@ -84,10 +104,45 @@ clutter_text_truncate_selection (ClutterText *ttext, const gchar *commandline, ClutterEvent *event); -G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_LABEL); +G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR); + +struct _LayoutCache +{ + /* Cached layout. Pango internally caches the computed extents when + they are requested so there is no need to cache that as well */ + PangoLayout *layout; + + /* The width that used to generate this layout */ + ClutterUnit width; + + /* A number representing the age of this cache (so that when a new + layout is needed the last used cache is replaced) */ + guint age; +}; struct _ClutterTextPrivate { + PangoFontDescription *font_desc; + + gchar *text; + gchar *font_name; + + ClutterColor text_color; + + LayoutCache cached_layouts[N_CACHED_LAYOUTS]; + guint cache_age; + + PangoAttrList *attrs; + PangoAttrList *effective_attrs; + + guint alignment : 2; + guint wrap : 1; + guint use_underline : 1; + guint use_markup : 1; + guint ellipsize : 3; + guint single_line_mode : 1; + guint wrap_mode : 3; + guint justify : 1; guint editable : 1; guint cursor_visible : 1; guint activatable : 1; @@ -111,14 +166,20 @@ struct _ClutterTextPrivate */ }; -#define CLUTTER_TEXT_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - CLUTTER_TYPE_TEXT, \ - ClutterTextPrivate)) - enum { PROP_0, + + PROP_FONT_NAME, + PROP_TEXT, + PROP_COLOR, + PROP_ATTRIBUTES, + PROP_USE_MARKUP, + PROP_ALIGNMENT, + PROP_WRAP, + PROP_WRAP_MODE, + PROP_JUSTIFY, + PROP_ELLIPSIZE, PROP_POSITION, PROP_SELECTION_BOUND, PROP_CURSOR_VISIBLE, @@ -134,10 +195,11 @@ enum TEXT_CHANGED, CURSOR_EVENT, ACTIVATE, + LAST_SIGNAL }; -static guint label_signals[LAST_SIGNAL] = { 0, }; +static guint text_signals[LAST_SIGNAL] = { 0, }; #define offset_real(text, pos) \ (pos==-1?g_utf8_strlen(text, -1):pos) \ @@ -228,11 +290,164 @@ static void init_mappings (ClutterText *ttext) clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter,0,"activate"); } +static PangoLayout * +clutter_text_create_layout_no_cache (ClutterText *text, + ClutterUnit allocation_width) +{ + ClutterTextPrivate *priv = text->priv; + PangoLayout *layout; + + if (G_UNLIKELY (_context == NULL)) + _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + + layout = pango_layout_new (_context); + + pango_layout_set_font_description (layout, priv->font_desc); + + if (priv->effective_attrs) + pango_layout_set_attributes (layout, priv->effective_attrs); + + pango_layout_set_alignment (layout, priv->alignment); + pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); + pango_layout_set_justify (layout, priv->justify); + + if (priv->text) + { + if (!priv->use_markup) + pango_layout_set_text (layout, priv->text, -1); + else + pango_layout_set_markup (layout, priv->text, -1); + } + + if (allocation_width > 0 && + (priv->ellipsize != PANGO_ELLIPSIZE_NONE || priv->wrap)) + { + int layout_width, layout_height; + + pango_layout_get_size (layout, &layout_width, &layout_height); + + /* No need to set ellipsize or wrap if we already have enough + * space, since we don't want to make the layout wider than it + * would be otherwise. + */ + + if (CLUTTER_UNITS_FROM_PANGO_UNIT (layout_width) > allocation_width) + { + if (priv->ellipsize != PANGO_ELLIPSIZE_NONE) + { + gint width; + + width = allocation_width > 0 + ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) + : -1; + + pango_layout_set_ellipsize (layout, priv->ellipsize); + pango_layout_set_width (layout, width); + } + else if (priv->wrap) + { + gint width; + + width = allocation_width > 0 + ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) + : -1; + + pango_layout_set_wrap (layout, priv->wrap_mode); + pango_layout_set_width (layout, width); + } + } + } + + return layout; +} + +static void +clutter_text_dirty_cache (ClutterText *text) +{ + ClutterTextPrivate *priv = text->priv; + int i; + + /* Delete the cached layouts so they will be recreated the next time + they are needed */ + for (i = 0; i < N_CACHED_LAYOUTS; i++) + if (priv->cached_layouts[i].layout) + { + g_object_unref (priv->cached_layouts[i].layout); + priv->cached_layouts[i].layout = NULL; + } +} + +/* + * clutter_text_create_layout: + * @text: a #ClutterText + * @allocation_width: the allocation width + * + * Like clutter_text_create_layout_no_cache(), but will also ensure + * the glyphs cache. If a previously cached layout generated using the + * same width is available then that will be used instead of + * generating a new one. + */ +static PangoLayout * +clutter_text_create_layout (ClutterText *text, + ClutterUnit allocation_width) +{ + ClutterTextPrivate *priv = text->priv; + LayoutCache *oldest_cache = priv->cached_layouts; + gboolean found_free_cache = FALSE; + int i; + + /* Search for a cached layout with the same width and keep track of + the oldest one */ + for (i = 0; i < N_CACHED_LAYOUTS; i++) + { + if (priv->cached_layouts[i].layout == NULL) + { + /* Always prefer free cache spaces */ + found_free_cache = TRUE; + oldest_cache = priv->cached_layouts + i; + } + /* If this cached layout is using the same width then we can + just return that directly */ + else if (priv->cached_layouts[i].width == allocation_width) + { + CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache hit for width %i", + text, + CLUTTER_UNITS_TO_DEVICE (allocation_width)); + + return priv->cached_layouts[i].layout; + } + else if (!found_free_cache && + (priv->cached_layouts[i].age < oldest_cache->age)) + { + oldest_cache = priv->cached_layouts + i; + } + } + + CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for width %i", + text, + CLUTTER_UNITS_TO_DEVICE (allocation_width)); + + /* If we make it here then we didn't have a cached version so we + need to recreate the layout */ + if (oldest_cache->layout) + g_object_unref (oldest_cache->layout); + + oldest_cache->layout = + clutter_text_create_layout_no_cache (text, allocation_width); + + cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout); + + /* Mark the 'time' this cache was created and advance the time */ + oldest_cache->age = priv->cache_age++; + oldest_cache->width = allocation_width; + + return oldest_cache->layout; +} static gint clutter_text_coords_to_position (ClutterText *text, - gint x, - gint y) + gint x, + gint y) { gint index_; gint px, py; @@ -241,7 +456,7 @@ clutter_text_coords_to_position (ClutterText *text, px = x * PANGO_SCALE; py = y * PANGO_SCALE; - pango_layout_xy_to_index (clutter_label_get_layout (CLUTTER_LABEL (text)), + pango_layout_xy_to_index (clutter_text_get_layout (text), px, py, &index_, &trailing); return index_ + trailing; @@ -259,7 +474,7 @@ clutter_text_position_to_coords (ClutterText *ttext, PangoRectangle rect; const gchar *text; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); priv = ttext->priv; @@ -276,7 +491,7 @@ clutter_text_position_to_coords (ClutterText *ttext, index_ = strlen (text); pango_layout_get_cursor_pos ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), index_, &rect, NULL); if (x) @@ -304,7 +519,7 @@ clutter_text_ensure_cursor_position (ClutterText *ttext) priv->cursor_pos.width = 2; priv->cursor_pos.height = cursor_height; - g_signal_emit (ttext, label_signals[CURSOR_EVENT], 0, &priv->cursor_pos); + g_signal_emit (ttext, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } gint @@ -318,7 +533,6 @@ void clutter_text_set_cursor_position (ClutterText *ttext, gint position) { - ClutterLabel *label = CLUTTER_LABEL (ttext); const gchar *text; ClutterTextPrivate *priv; gint len; @@ -327,7 +541,7 @@ clutter_text_set_cursor_position (ClutterText *ttext, priv = ttext->priv; - text = clutter_label_get_text (label); + text = clutter_text_get_text (ttext); if (text == NULL) return; @@ -347,7 +561,7 @@ clutter_text_truncate_selection (ClutterText *ttext, const gchar *commandline, ClutterEvent *event) { - const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + const gchar *utf8 = clutter_text_get_text (ttext); ClutterTextPrivate *priv; gint start_index; gint end_index; @@ -397,14 +611,14 @@ clutter_text_insert_unichar (ClutterText *ttext, g_object_ref (ttext); - old_text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + old_text = clutter_text_get_text (ttext); new = g_string_new (old_text); pos = offset_to_bytes (old_text, priv->position); new = g_string_insert_unichar (new, pos, wc); - clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + clutter_text_set_text (ttext, new->str); if (priv->position >= 0) { @@ -416,7 +630,7 @@ clutter_text_insert_unichar (ClutterText *ttext, g_object_unref (ttext); - g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); + g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); } void @@ -433,7 +647,7 @@ clutter_text_delete_text (ClutterText *ttext, g_return_if_fail (CLUTTER_IS_TEXT (ttext)); priv = ttext->priv; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); if (end_pos == -1) { @@ -450,10 +664,10 @@ clutter_text_delete_text (ClutterText *ttext, new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - clutter_label_set_text (CLUTTER_LABEL (ttext), new->str); + clutter_text_set_text (ttext, new->str); g_string_free (new, TRUE); - g_signal_emit (G_OBJECT (ttext), label_signals[TEXT_CHANGED], 0); + g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); } static void @@ -488,6 +702,15 @@ clutter_text_set_property (GObject *gobject, switch (prop_id) { + case PROP_TEXT: + clutter_text_set_text (ttext, g_value_get_string (value)); + break; + case PROP_COLOR: + clutter_text_set_color (ttext, clutter_value_get_color (value)); + break; + case PROP_FONT_NAME: + clutter_text_set_font_name (ttext, g_value_get_string (value)); + break; case PROP_POSITION: clutter_text_set_cursor_position (ttext, g_value_get_int (value)); break; @@ -528,15 +751,20 @@ clutter_text_get_property (GObject *gobject, switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + case PROP_FONT_NAME: + g_value_set_string (value, priv->font_name); + break; + case PROP_COLOR: + clutter_value_set_color (value, &priv->text_color); + break; case PROP_CURSOR_VISIBLE: g_value_set_boolean (value, priv->cursor_visible); break; case PROP_CURSOR_COLOR: - { - ClutterColor color; - clutter_text_get_cursor_color (CLUTTER_TEXT (gobject), &color); - g_value_set_boxed (value, &color); - } + clutter_value_set_color (value, &priv->cursor_color); break; case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); @@ -560,10 +788,8 @@ clutter_text_get_property (GObject *gobject, } static void -cursor_paint (ClutterActor *actor, - gpointer user_data) +cursor_paint (ClutterText *ttext) { - ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; if (priv->editable && @@ -579,7 +805,7 @@ cursor_paint (ClutterActor *actor, else { ClutterColor color; - clutter_label_get_color (CLUTTER_LABEL (actor), &color); + clutter_text_get_color (ttext, &color); cogl_set_source_color4ub (color.red, color.green, color.blue, @@ -603,7 +829,7 @@ cursor_paint (ClutterActor *actor, gint lines; gint start_index; gint end_index; - const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (ttext)); + const gchar *utf8 = clutter_text_get_text (ttext); start_index = offset_to_bytes (utf8, priv->position); end_index = offset_to_bytes (utf8, priv->selection_bound); @@ -615,7 +841,7 @@ cursor_paint (ClutterActor *actor, end_index = temp; } - PangoLayout *layout = clutter_label_get_layout (CLUTTER_LABEL (ttext)); + PangoLayout *layout = clutter_text_get_layout (ttext); lines = pango_layout_get_line_count (layout); gint line_no; for (line_no = 0; line_no < lines; line_no++) @@ -666,7 +892,7 @@ clutter_text_press (ClutterActor *actor, gint index_; const gchar *text; - text = clutter_label_get_text (CLUTTER_LABEL (actor)); + text = clutter_text_get_text (ttext); x = CLUTTER_UNITS_FROM_INT (bev->x); y = CLUTTER_UNITS_FROM_INT (bev->y); @@ -712,7 +938,7 @@ clutter_text_motion (ClutterActor *actor, return FALSE; } - text = clutter_label_get_text (CLUTTER_LABEL (actor)); + text = clutter_text_get_text (ttext); x = CLUTTER_UNITS_FROM_INT (mev->x); y = CLUTTER_UNITS_FROM_INT (mev->y); @@ -750,13 +976,124 @@ clutter_text_release (ClutterActor *actor, return FALSE; } +static void +clutter_text_paint (ClutterActor *self) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterTextPrivate *priv = text->priv; + PangoLayout *layout; + ClutterActorBox alloc = { 0, }; + CoglColor color = { 0, }; + + if (priv->font_desc == NULL || priv->text == NULL) + { + CLUTTER_NOTE (ACTOR, "desc: %p, text %p", + priv->font_desc ? priv->font_desc : 0x0, + priv->text ? priv->text : 0x0); + return; + } + + cursor_paint (text); + + CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text); + + clutter_actor_get_allocation_box (self, &alloc); + layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + + cogl_color_set_from_4ub (&color, + priv->text_color.red, + priv->text_color.green, + priv->text_color.blue, + clutter_actor_get_paint_opacity (self)); + cogl_pango_render_layout (layout, 0, 0, &color, 0); +} + +static void +clutter_text_get_preferred_width (ClutterActor *self, + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterTextPrivate *priv = text->priv; + PangoRectangle logical_rect = { 0, }; + PangoLayout *layout; + ClutterUnit layout_width; + + layout = clutter_text_create_layout (text, -1); + + pango_layout_get_extents (layout, NULL, &logical_rect); + + layout_width = logical_rect.width > 0 + ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.width) + : 1; + + if (min_width_p) + { + if (priv->wrap || priv->ellipsize) + *min_width_p = 1; + else + *min_width_p = layout_width; + } + + if (natural_width_p) + *natural_width_p = layout_width; +} + +static void +clutter_text_get_preferred_height (ClutterActor *self, + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p) +{ + ClutterText *text = CLUTTER_TEXT (self); + + if (for_width == 0) + { + if (min_height_p) + *min_height_p = 0; + + if (natural_height_p) + *natural_height_p = 0; + } + else + { + PangoLayout *layout; + PangoRectangle logical_rect = { 0, }; + ClutterUnit height; + + layout = clutter_text_create_layout (text, for_width); + + pango_layout_get_extents (layout, NULL, &logical_rect); + height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.height); + + if (min_height_p) + *min_height_p = height; + + if (natural_height_p) + *natural_height_p = height; + } +} + +static void +clutter_text_allocate (ClutterActor *self, + const ClutterActorBox *box, + gboolean origin_changed) +{ + ClutterText *text = CLUTTER_TEXT (self); + ClutterActorClass *parent_class; + + /* Ensure that there is a cached layout with the right width so that + we don't need to create the text during the paint run */ + clutter_text_create_layout (text, box->x2 - box->x1); + + parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); + parent_class->allocate (self, box, origin_changed); +} + static void clutter_text_constructed (GObject *object) { - g_signal_connect (CLUTTER_ACTOR (object), - "paint", G_CALLBACK (cursor_paint), - NULL); - if (G_OBJECT_CLASS (clutter_text_parent_class)->constructed != NULL) G_OBJECT_CLASS (clutter_text_parent_class)->constructed (object); } @@ -766,17 +1103,51 @@ clutter_text_class_init (ClutterTextClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GParamSpec *pspec; gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; gobject_class->constructed = clutter_text_constructed; gobject_class->finalize = clutter_text_finalize; + actor_class->paint = clutter_text_paint; + actor_class->get_preferred_width = clutter_text_get_preferred_width; + 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->button_press_event = clutter_text_press; actor_class->button_release_event = clutter_text_release; actor_class->motion_event = clutter_text_motion; + /** + * ClutterText:font-name: + * + * The font to be used by the #ClutterText, as a string + * that can be parsed by pango_font_description_from_string(). + * + * Since: 0.2 + */ + pspec = g_param_spec_string ("font-name", + "Font Name", + "The font to be used by the text", + NULL, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); + + pspec = g_param_spec_string ("text", + "Text", + "The text to render", + NULL, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_TEXT, pspec); + + pspec = clutter_param_spec_color ("color", + "Font Color", + "Color of the font used by the text", + &default_text_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_COLOR, pspec); + /** * ClutterText:editable: * @@ -876,7 +1247,7 @@ clutter_text_class_init (ClutterTextClass *klass) * * The ::text-changed signal is emitted after @entry's text changes */ - label_signals[TEXT_CHANGED] = + text_signals[TEXT_CHANGED] = g_signal_new ("text-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, @@ -886,7 +1257,7 @@ clutter_text_class_init (ClutterTextClass *klass) G_TYPE_NONE, 0); - label_signals[CURSOR_EVENT] = + text_signals[CURSOR_EVENT] = g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, @@ -906,7 +1277,7 @@ clutter_text_class_init (ClutterTextClass *klass) * * Since: 0.4 */ - label_signals[ACTIVATE] = + text_signals[ACTIVATE] = g_signal_new ("activate", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, @@ -925,19 +1296,21 @@ clutter_text_init (ClutterText *self) ClutterTextPrivate *priv; self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); + priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; priv->cursor_color_set = FALSE; + init_commands (self); /* FIXME: free */ init_mappings (self); /* FIXME: free */ } ClutterActor * clutter_text_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) + const gchar *text, + const ClutterColor *color) { return g_object_new (CLUTTER_TYPE_TEXT, "font-name", font_name, @@ -958,81 +1331,81 @@ clutter_text_new_with_text (const gchar *font_name, void -clutter_text_set_editable (ClutterText *label, - gboolean editable) +clutter_text_set_editable (ClutterText *text, + gboolean editable) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->editable = editable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_editable (ClutterText *label) +clutter_text_get_editable (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->editable; } void -clutter_text_set_selectable (ClutterText *label, +clutter_text_set_selectable (ClutterText *text, gboolean selectable) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->selectable = selectable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_selectable (ClutterText *label) +clutter_text_get_selectable (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->selectable; } void -clutter_text_set_activatable (ClutterText *label, +clutter_text_set_activatable (ClutterText *text, gboolean activatable) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->activatable = activatable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_activatable (ClutterText *label) +clutter_text_get_activatable (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->activatable; } void -clutter_text_set_cursor_visible (ClutterText *label, +clutter_text_set_cursor_visible (ClutterText *text, gboolean cursor_visible) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; priv->cursor_visible = cursor_visible; - clutter_actor_queue_redraw (CLUTTER_ACTOR (label)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); } gboolean -clutter_text_get_cursor_visible (ClutterText *label) +clutter_text_get_cursor_visible (ClutterText *text) { ClutterTextPrivate *priv; - priv = label->priv; + priv = text->priv; return priv->cursor_visible; } @@ -1042,7 +1415,7 @@ clutter_text_set_cursor_color (ClutterText *text, { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (CLUTTER_IS_TEXT (text)); g_return_if_fail (color != NULL); priv = text->priv; @@ -1067,7 +1440,7 @@ clutter_text_get_cursor_color (ClutterText *text, { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_LABEL (text)); + g_return_if_fail (CLUTTER_IS_TEXT (text)); g_return_if_fail (color != NULL); priv = text->priv; @@ -1091,7 +1464,7 @@ clutter_text_get_selection (ClutterText *text) { ClutterTextPrivate *priv; - const gchar *utf8 = clutter_label_get_text (CLUTTER_LABEL (text)); + const gchar *utf8 = clutter_text_get_text (text); gchar *str; gint len; gint start_index; @@ -1148,7 +1521,7 @@ clutter_text_action_activate (ClutterText *ttext, const gchar *commandline, ClutterEvent *event) { - g_signal_emit (G_OBJECT (ttext), label_signals[ACTIVATE], 0); + g_signal_emit (G_OBJECT (ttext), text_signals[ACTIVATE], 0); return TRUE; } @@ -1167,7 +1540,7 @@ clutter_text_action_move_left (ClutterText *ttext, ClutterTextPrivate *priv = ttext->priv; gint pos = priv->position; gint len; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (pos != 0 && len !=0) { @@ -1198,7 +1571,7 @@ clutter_text_action_move_right (ClutterText *ttext, gint pos; gint len; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); pos = priv->position; if (pos != -1 && len !=0) @@ -1231,10 +1604,10 @@ clutter_text_action_move_up (ClutterText *ttext, const gchar *text; PangoLayoutLine *layout_line; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), offset_to_bytes (text, priv->position), 0, &line_no, @@ -1250,7 +1623,7 @@ clutter_text_action_move_up (ClutterText *ttext, return FALSE; layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no); if (!layout_line) @@ -1282,10 +1655,10 @@ clutter_text_action_move_down (ClutterText *ttext, const gchar *text; PangoLayoutLine *layout_line; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), offset_to_bytes (text, priv->position), 0, &line_no, @@ -1297,7 +1670,7 @@ clutter_text_action_move_down (ClutterText *ttext, priv->x_pos = x; layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no + 1); if (!layout_line) @@ -1358,18 +1731,18 @@ clutter_text_action_move_start_line (ClutterText *ttext, PangoLayoutLine *layout_line; gint position; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), offset_to_bytes (text, priv->position), 0, &line_no, NULL); layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); @@ -1397,19 +1770,19 @@ clutter_text_action_move_end_line (ClutterText *ttext, PangoLayoutLine *layout_line; gint position; - text = clutter_label_get_text (CLUTTER_LABEL (ttext)); + text = clutter_text_get_text (ttext); index_ = offset_to_bytes (text, priv->position); pango_layout_index_to_line_x ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), index_, 0, &line_no, NULL); layout_line = pango_layout_get_line_readonly ( - clutter_label_get_layout (CLUTTER_LABEL (ttext)), + clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); @@ -1439,7 +1812,7 @@ clutter_text_action_delete_next (ClutterText *ttext, return TRUE; priv = ttext->priv; pos = priv->position; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (len && pos != -1 && pos < len) clutter_text_delete_text (ttext, pos, pos+1);; @@ -1459,7 +1832,7 @@ clutter_text_action_delete_previous (ClutterText *ttext, return TRUE; priv = ttext->priv; pos = priv->position; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (pos != 0 && len !=0) { @@ -1575,3 +1948,153 @@ clutter_text_key_press (ClutterActor *actor, return FALSE; } +G_CONST_RETURN gchar * +clutter_text_get_font_name (ClutterText *text) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + return text->priv->font_name; +} + +void +clutter_text_set_font_name (ClutterText *text, + const gchar *font_name) +{ + ClutterTextPrivate *priv; + PangoFontDescription *desc; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + + if (!font_name || font_name[0] == '\0') + font_name = DEFAULT_FONT_NAME; + + priv = text->priv; + + if (priv->font_name && strcmp (priv->font_name, font_name) == 0) + return; + + desc = pango_font_description_from_string (font_name); + if (!desc) + { + g_warning ("Attempting to create a PangoFontDescription for " + "font name `%s', but failed.", + font_name); + return; + } + + g_free (priv->font_name); + priv->font_name = g_strdup (font_name); + + if (priv->font_desc) + pango_font_description_free (priv->font_desc); + + priv->font_desc = desc; + + clutter_text_dirty_cache (text); + + if (priv->text && priv->text[0] != '\0') + clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + + g_object_notify (G_OBJECT (text), "font-name"); +} + +G_CONST_RETURN gchar * +clutter_text_get_text (ClutterText *text) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + return text->priv->text; +} + +void +clutter_text_set_text (ClutterText *text, + const gchar *str) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + + priv = text->priv; + + g_free (priv->text); + priv->text = g_strdup (str); + + clutter_text_dirty_cache (text); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + + g_object_notify (G_OBJECT (text), "text"); +} + +PangoLayout * +clutter_text_get_layout (ClutterText *text) +{ + ClutterUnit width; + + g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + + width = clutter_actor_get_widthu (CLUTTER_ACTOR (text)); + + return clutter_text_create_layout (text, width); +} + +void +clutter_text_set_color (ClutterText *text, + const ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + priv->text_color = *color; + + if (CLUTTER_ACTOR_IS_VISIBLE (text)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + + g_object_notify (G_OBJECT (text), "color"); +} + +void +clutter_text_get_color (ClutterText *text, + ClutterColor *color) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (color != NULL); + + priv = text->priv; + + *color = priv->text_color; +} + +gboolean +clutter_text_get_line_wrap (ClutterText *text) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (text), FALSE); + + return text->priv->wrap; +} + +void +clutter_text_set_line_wrap (ClutterText *text, + gboolean line_wrap) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (text)); + + priv = text->priv; + + if (priv->wrap != line_wrap) + { + priv->wrap = line_wrap; + + clutter_text_dirty_cache (text); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + } +} From 5ac16652df4fc165c6237464e6df250687bf023f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:18:05 +0000 Subject: [PATCH 005/108] Update the ClutterText interactive test Use ClutterText's own API instead of relying on the Label's API. ClutterText is not meant to be a subclass of ClutterLabel. --- tests/interactive/test-text.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c index 6a0393f44..ce262dcfc 100644 --- a/tests/interactive/test-text.c +++ b/tests/interactive/test-text.c @@ -22,7 +22,7 @@ select_all (ClutterText *ttext, ClutterEvent *event) { gint len; - len = g_utf8_strlen (clutter_label_get_text (CLUTTER_LABEL (ttext)), -1); + len = g_utf8_strlen (clutter_text_get_text (ttext), -1); clutter_text_set_cursor_position (ttext, 0); clutter_text_set_selection_bound (ttext, len); @@ -126,7 +126,7 @@ test_text_main (gint argc, clutter_container_add (CLUTTER_CONTAINER (stage), text, NULL); clutter_actor_set_position (text, 40, 30); clutter_actor_set_width (text, 1024); - clutter_label_set_line_wrap (CLUTTER_LABEL (text), TRUE); + clutter_text_set_line_wrap (CLUTTER_TEXT (text), TRUE); clutter_actor_set_reactive (text, TRUE); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text); @@ -159,11 +159,11 @@ test_text_main (gint argc, { gchar *utf8; g_file_get_contents (argv[1], &utf8, NULL, NULL); - clutter_label_set_text (CLUTTER_LABEL (text), utf8); + clutter_text_set_text (CLUTTER_TEXT (text), utf8); } else { - clutter_label_set_text (CLUTTER_LABEL (text), runes); + clutter_text_set_text (CLUTTER_TEXT (text), runes); } g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL); From 437f96982e5754f25630570818d4a5173bd9a8ce Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:20:14 +0000 Subject: [PATCH 006/108] Expose the Text accessors as public API Fix up the header to expose ClutterText accessors for the main properties, matching ClutterLabel. --- clutter/clutter-text.c | 8 ------- clutter/clutter-text.h | 47 +++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 34ee928e0..87a5b855d 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -83,14 +83,6 @@ static void clutter_text_get_property (GObject *gobject, GParamSpec *pspec); static void clutter_text_finalize (GObject *gobject); -G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); -void clutter_text_set_text (ClutterText *text, const gchar *str); -PangoLayout *clutter_text_get_layout (ClutterText *text); -void clutter_text_set_color (ClutterText *text, const ClutterColor *color); -void clutter_text_get_color (ClutterText *text, ClutterColor *color); -void clutter_text_set_font_name (ClutterText *text, const gchar *font_name); -G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); - static void init_commands (ClutterText *ttext); static void init_mappings (ClutterText *ttext); diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 908275cc2..cd901bffa 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -18,11 +18,13 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + #ifndef __CLUTTER_TEXT_H__ #define __CLUTTER_TEXT_H__ @@ -31,27 +33,12 @@ G_BEGIN_DECLS -#define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) - -#define CLUTTER_TEXT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_TEXT, ClutterText)) - -#define CLUTTER_TEXT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_TEXT, ClutterTextClass)) - -#define CLUTTER_IS_TEXT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_TEXT)) - -#define CLUTTER_IS_TEXT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_TEXT)) - -#define CLUTTER_TEXT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_TEXT, ClutterTextClass)) +#define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) +#define CLUTTER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXT, ClutterText)) +#define CLUTTER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TEXT, ClutterTextClass)) +#define CLUTTER_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXT)) +#define CLUTTER_IS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TEXT)) +#define CLUTTER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TEXT, ClutterTextClass)) typedef struct _ClutterText ClutterText; typedef struct _ClutterTextPrivate ClutterTextPrivate; @@ -83,6 +70,18 @@ ClutterActor *clutter_text_new_full (const gchar *font_name, ClutterActor *clutter_text_new_with_text (const gchar *font_name, const gchar *text); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); +void clutter_text_set_text (ClutterText *text, + const gchar *str); +PangoLayout * clutter_text_get_layout (ClutterText *text); +void clutter_text_set_color (ClutterText *text, + const ClutterColor *color); +void clutter_text_get_color (ClutterText *text, + ClutterColor *color); +void clutter_text_set_font_name (ClutterText *text, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); + void clutter_text_set_editable (ClutterText *label, gboolean editable); gboolean clutter_text_get_editable (ClutterText *label); From 40fb75052ba11b80b41590f17ab3b194df11ae3f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:21:39 +0000 Subject: [PATCH 007/108] Merge Label and Entry API into ClutterText ClutterText should expose both ClutterLabel and ClutterEntry functionality using an API that matches the one provided by those two classes. --- clutter/clutter-text.h | 142 +++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 47 deletions(-) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index cd901bffa..054c941c3 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -46,69 +46,117 @@ typedef struct _ClutterTextClass ClutterTextClass; struct _ClutterText { - ClutterLabel parent_instance; - /*< private >*/ + ClutterActor parent_instance; + ClutterTextPrivate *priv; }; struct _ClutterTextClass { - ClutterLabelClass parent_class; + ClutterActorClass parent_class; - void (* text_changed) (ClutterText *text); - void (* activate) (ClutterText *text); - void (* cursor_event) (ClutterText *text, + void (* text_changed) (ClutterText *self); + void (* activate) (ClutterText *self); + void (* cursor_event) (ClutterText *self, ClutterGeometry *geometry); + + void (* _clutter_reserved1) (void); + void (* _clutter_reserved2) (void); + void (* _clutter_reserved3) (void); + void (* _clutter_reserved4) (void); + void (* _clutter_reserved5) (void); + void (* _clutter_reserved6) (void); + void (* _clutter_reserved7) (void); + void (* _clutter_reserved8) (void); }; GType clutter_text_get_type (void) G_GNUC_CONST; -ClutterActor *clutter_text_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); -ClutterActor *clutter_text_new_with_text (const gchar *font_name, - const gchar *text); +ClutterActor * clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color); +ClutterActor * clutter_text_new_with_text (const gchar *font_name, + const gchar *text); -G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *text); -void clutter_text_set_text (ClutterText *text, - const gchar *str); -PangoLayout * clutter_text_get_layout (ClutterText *text); -void clutter_text_set_color (ClutterText *text, - const ClutterColor *color); -void clutter_text_get_color (ClutterText *text, - ClutterColor *color); -void clutter_text_set_font_name (ClutterText *text, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *text); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *self); +void clutter_text_set_text (ClutterText *self, + const gchar *text); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_font_name (ClutterText *self, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *self); -void clutter_text_set_editable (ClutterText *label, - gboolean editable); -gboolean clutter_text_get_editable (ClutterText *label); -void clutter_text_set_activatable (ClutterText *label, - gboolean activatable); -gboolean clutter_text_get_activatable (ClutterText *label); +void clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode); +PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); +void clutter_text_set_line_wrap (ClutterText *self, + gboolean wrap); +gboolean clutter_text_get_line_wrap (ClutterText *self); +void clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode); +PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs); +PangoAttrList * clutter_text_get_attributes (ClutterText *self); +void clutter_text_set_use_markup (ClutterText *self, + gboolean setting); +gboolean clutter_text_get_use_markup (ClutterText *self); +void clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment); +PangoAlignment clutter_text_get_alignment (ClutterText *self); +void clutter_text_set_justify (ClutterText *self, + gboolean justify); +gboolean clutter_text_get_justify (ClutterText *self); -gint clutter_text_get_cursor_position (ClutterText *label); -void clutter_text_set_cursor_position (ClutterText *label, - gint position); -void clutter_text_set_cursor_visible (ClutterText *label, - gboolean cursor_visible); -gboolean clutter_text_get_cursor_visible (ClutterText *label); -void clutter_text_set_cursor_color (ClutterText *text, - const ClutterColor *color); -void clutter_text_get_cursor_color (ClutterText *text, - ClutterColor *color); -void clutter_text_set_selectable (ClutterText *label, - gboolean selectable); -gboolean clutter_text_get_selectable (ClutterText *label); -void clutter_text_set_selection_bound (ClutterText *text, - gint selection_bound); -gint clutter_text_get_selection_bound (ClutterText *text); -gchar * clutter_text_get_selection (ClutterText *text); -void clutter_text_insert_unichar (ClutterText *ttext, - gunichar wc); +void clutter_text_insert_unichar (ClutterText *self, + gunichar wc); +void clutter_text_delete_chars (ClutterText *self, + guint len); +void clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position); +void clutter_text_delete_text (ClutterText *self, + gssize start_pos, + gssize end_pos); +void clutter_text_set_editable (ClutterText *self, + gboolean editable); +gboolean clutter_text_get_editable (ClutterText *self); +void clutter_text_set_activatable (ClutterText *self, + gboolean activatable); +gboolean clutter_text_get_activatable (ClutterText *self); +gint clutter_text_get_cursor_position (ClutterText *self); +void clutter_text_set_cursor_position (ClutterText *self, + gint position); +void clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible); +gboolean clutter_text_get_cursor_visible (ClutterText *self); +void clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_selectable (ClutterText *self, + gboolean selectable); +gboolean clutter_text_get_selectable (ClutterText *self); +void clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound); +gint clutter_text_get_selection_bound (ClutterText *self); +gchar * clutter_text_get_selection (ClutterText *self); +void clutter_text_set_visibility (ClutterText *self, + gboolean visible); +gboolean clutter_text_get_visibility (ClutterText *self); +void clutter_text_set_invisible_char (ClutterText *self, + gunichar wc); +gunichar clutter_text_get_invisible_char (ClutterText *self); +void clutter_text_set_max_length (ClutterText *self, + gint max); +gint clutter_text_get_max_length (ClutterText *self); /* add a custom action that can be used in keybindings */ void clutter_text_add_action (ClutterText *ttext, From 430d1cf347beeaa619651f2a227d00e053b6b2da Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:22:46 +0000 Subject: [PATCH 008/108] Add Actor::grab_key_focus() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1003 - Add clutter_actor_take_key_focus() The grab_key_focus() method is just a simple wrapper around clutter_stage_take_key_focus() that removes the need to get the ClutterStage of an actor in order to set the key focus. Based on a patch by Xan López. Signed-off-by: Emmanuele Bassi Signed-off-by: Øyvind Kolås --- clutter/clutter-actor.c | 25 +++++++++++++++++++++++++ clutter/clutter-actor.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 424144436..6a30c1c11 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7564,3 +7564,28 @@ clutter_actor_allocate_preferred_size (ClutterActor *self, clutter_actor_allocate (self, &actor_box, absolute_origin_changed); } + +/** + * clutter_actor_grab_key_focus: + * @self: a #ClutterActor + * + * Sets the key focus of the #ClutterStage including @self + * to this #ClutterActor. + * + * Since: 1.0 + */ +void +clutter_actor_grab_key_focus (ClutterActor *self) +{ + ClutterActor *parent; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + parent = clutter_actor_get_parent (self); + if (!parent) + return; + + parent = clutter_actor_get_stage (self); + if (parent && CLUTTER_IS_STAGE (parent)) + clutter_stage_set_key_focus (CLUTTER_STAGE (parent), self); +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index e964a9ef0..1874e283b 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -561,6 +561,8 @@ void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, gboolean clutter_actor_get_paint_visibility (ClutterActor *self); +void clutter_actor_grab_key_focus (ClutterActor *self); + G_END_DECLS #endif /* _HAVE_CLUTTER_ACTOR_H */ From c5f51f7027dbbac373587621a52fc6f95f529e69 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:31:48 +0000 Subject: [PATCH 009/108] Add Text::get_chars() method declaration Add the declaration for a clutter_text_get_chars() function that returns a portion of the contents of a Text actor. --- clutter/clutter-text.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 054c941c3..65c919796 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -124,6 +124,9 @@ void clutter_text_insert_text (ClutterText *self void clutter_text_delete_text (ClutterText *self, gssize start_pos, gssize end_pos); +gchar * clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos); void clutter_text_set_editable (ClutterText *self, gboolean editable); gboolean clutter_text_get_editable (ClutterText *self); From fc168a34823567caead8e7cbfcf91b3f47b229e8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:33:10 +0000 Subject: [PATCH 010/108] Merge the ClutterLabel properties into ClutterText ClutterText should have the same properties as ClutterLabel. While at it, we can finally fix the disconnect between the wrap and wrap-mode properties and its accessors, that we perpetuated from GtkLabel. The ClutterText:line-wrap property and ClutterText:line-wrap-mode are mapped to the set_line_wrap(), get_line_wrap() and set_line_wrap_mode(), get_line_wrap_mode() accessor functions respectively. This should simplify bindings the Vala ones that map a property into a method. --- clutter/clutter-text.c | 1286 +++++++++++++++++++++++++++------------- 1 file changed, 873 insertions(+), 413 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 87a5b855d..4f2e3d7d6 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -46,11 +46,12 @@ #define DEFAULT_FONT_NAME "Sans 10" /* We need at least three cached layouts to run the allocation without - regenerating a new layout. First the layout will be generated at - full width to get the preferred width, then it will be generated at - the preferred width to get the preferred height and then it might - be regenerated at a different width to get the height for the - actual allocated width */ + * regenerating a new layout. First the layout will be generated at + * full width to get the preferred width, then it will be generated at + * the preferred width to get the preferred height and then it might + * be regenerated at a different width to get the height for the + * actual allocated width + */ #define N_CACHED_LAYOUTS 3 #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) @@ -63,52 +64,22 @@ static PangoContext *_context = NULL; static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; static const ClutterColor default_text_color = { 0, 0, 0, 255 }; -static gboolean clutter_text_key_press (ClutterActor *actor, - ClutterKeyEvent *kev); -static gboolean clutter_text_position_to_coords (ClutterText *ttext, - gint position, - gint *x, - gint *y, - gint *cursor_height); -static gint clutter_text_coords_to_position (ClutterText *text, - gint x, - gint y); -static void clutter_text_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void clutter_text_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void clutter_text_finalize (GObject *gobject); - -static void init_commands (ClutterText *ttext); -static void init_mappings (ClutterText *ttext); - -void -clutter_text_delete_text (ClutterText *ttext, - gssize start_pos, - gssize end_pos); - -static gboolean -clutter_text_truncate_selection (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); - G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR); struct _LayoutCache { - /* Cached layout. Pango internally caches the computed extents when - they are requested so there is no need to cache that as well */ + /* Cached layout. Pango internally caches the computed extents + * when they are requested so there is no need to cache that as + * well + */ PangoLayout *layout; /* The width that used to generate this layout */ ClutterUnit width; - /* A number representing the age of this cache (so that when a new - layout is needed the last used cache is replaced) */ + /* A number representing the age of this cache (so that when a + * new layout is needed the last used cache is replaced) + */ guint age; }; @@ -124,8 +95,8 @@ struct _ClutterTextPrivate LayoutCache cached_layouts[N_CACHED_LAYOUTS]; guint cache_age; - PangoAttrList *attrs; - PangoAttrList *effective_attrs; + PangoAttrList *attrs; + PangoAttrList *effective_attrs; guint alignment : 2; guint wrap : 1; @@ -142,20 +113,28 @@ struct _ClutterTextPrivate guint in_select_drag : 1; guint cursor_color_set : 1; - gint position; /* current cursor position */ - gint selection_bound; - /* current 'other end of selection' position */ - gint x_pos; /* the x position in the pangolayout, used to - * avoid drifting when repeatedly moving up|down - */ - ClutterColor cursor_color; - ClutterGeometry cursor_pos; /* Where to draw the cursor */ + /* current cursor position */ + gint position; - GList *mappings; - GList *commands; /* each instance has it's own set of commands - so that actor specific actions can be added - to single actor classes - */ + /* current 'other end of selection' position */ + gint selection_bound; + + /* the x position in the pangolayout, used to + * avoid drifting when repeatedly moving up|down + */ + gint x_pos; + + /* Where to draw the cursor */ + ClutterGeometry cursor_pos; + ClutterColor cursor_color; + + ClutterColor selection_color; + + GList *mappings; + GList *commands; /* each instance has it's own set of commands + so that actor specific actions can be added + to single actor classes + */ }; enum @@ -165,11 +144,11 @@ enum PROP_FONT_NAME, PROP_TEXT, PROP_COLOR, - PROP_ATTRIBUTES, PROP_USE_MARKUP, + PROP_ATTRIBUTES, PROP_ALIGNMENT, - PROP_WRAP, - PROP_WRAP_MODE, + PROP_LINE_WRAP, + PROP_LINE_WRAP_MODE, PROP_JUSTIFY, PROP_ELLIPSIZE, PROP_POSITION, @@ -193,14 +172,15 @@ enum static guint text_signals[LAST_SIGNAL] = { 0, }; -#define offset_real(text, pos) \ - (pos==-1?g_utf8_strlen(text, -1):pos) \ +#define offset_real(text, pos) \ + (pos == -1 ? g_utf8_strlen (text, -1) : pos) -#define offset_to_bytes(text,pos)\ - (pos==-1?strlen(text):((gint)(g_utf8_offset_to_pointer (text, pos) - text))) +#define offset_to_bytes(text,pos) \ + (pos == -1 ? strlen (text) \ + : ((gint) (g_utf8_offset_to_pointer (text, pos) - text))) -#define bytes_to_offset(text, pos) \ - (g_utf8_pointer_to_offset (text, text + pos)) +#define bytes_to_offset(text, pos) \ + (g_utf8_pointer_to_offset (text, text + pos)) typedef struct TextCommand { @@ -216,51 +196,26 @@ typedef struct ClutterTextMapping { const gchar *action; } ClutterTextMapping; - void -clutter_text_mappings_clear (ClutterText *ttext) +clutter_text_mappings_clear (ClutterText *self) { - ClutterTextPrivate *priv = ttext->priv; - GList *iter; - for (iter = priv->mappings; iter; iter=iter->next) - { - g_free (iter->data); - } + ClutterTextPrivate *priv = self->priv; + + g_list_foreach (priv->mappings, (GFunc) g_free, NULL); g_list_free (priv->mappings); priv->mappings = NULL; } -void clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline) -{ - ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); - ClutterTextPrivate *priv = ttext->priv; - tmapping->keyval = keyval; - tmapping->state = state; - tmapping->action = commandline; - priv->mappings = g_list_append (priv->mappings, tmapping); -} +static void init_commands (ClutterText *ttext); -void clutter_text_add_action (ClutterText *ttext, - const gchar *name, - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event)) +static void +init_mappings (ClutterText *ttext) { - TextCommand *tcommand = g_new (TextCommand, 1); ClutterTextPrivate *priv = ttext->priv; - tcommand->name = name; - tcommand->func = func; - priv->commands = g_list_append (priv->commands, tcommand); -} -static void init_mappings (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; if (priv->mappings) return; + clutter_text_add_mapping (ttext, CLUTTER_Left, 0, "move-left"); clutter_text_add_mapping (ttext, CLUTTER_KP_Left, 0, "move-left"); clutter_text_add_mapping (ttext, CLUTTER_Right, 0, "move-right"); @@ -274,12 +229,12 @@ static void init_mappings (ClutterText *ttext) clutter_text_add_mapping (ttext, CLUTTER_KP_Home, 0, "move-start-line"); clutter_text_add_mapping (ttext, CLUTTER_End, 0, "move-end-line"); clutter_text_add_mapping (ttext, CLUTTER_KP_End, 0, "move-end-line"); - clutter_text_add_mapping (ttext, CLUTTER_BackSpace,0,"delete-previous"); + clutter_text_add_mapping (ttext, CLUTTER_BackSpace, 0 , "delete-previous"); clutter_text_add_mapping (ttext, CLUTTER_Delete, 0, "delete-next"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0,"delete-next"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0, "delete-next"); clutter_text_add_mapping (ttext, CLUTTER_Return, 0, "activate"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0,"activate"); - clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter,0,"activate"); + clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0, "activate"); + clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter, 0, "activate"); } static PangoLayout * @@ -514,56 +469,15 @@ clutter_text_ensure_cursor_position (ClutterText *ttext) g_signal_emit (ttext, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } -gint -clutter_text_get_cursor_position (ClutterText *ttext) +static inline gboolean +clutter_text_truncate_selection_internal (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (ttext), -1); - return ttext->priv->position; -} + ClutterTextPrivate *priv = self->priv; + gint start_index; + gint end_index; -void -clutter_text_set_cursor_position (ClutterText *ttext, - gint position) -{ - const gchar *text; - ClutterTextPrivate *priv; - gint len; - - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); - - priv = ttext->priv; - - text = clutter_text_get_text (ttext); - if (text == NULL) - return; - - len = g_utf8_strlen (text, -1); - - if (position < 0 || position >= len) - priv->position = -1; - else - priv->position = position; - - if (CLUTTER_ACTOR_IS_VISIBLE (ttext)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (ttext)); -} - -static gboolean -clutter_text_truncate_selection (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - const gchar *utf8 = clutter_text_get_text (ttext); - ClutterTextPrivate *priv; - gint start_index; - gint end_index; - - priv = ttext->priv; - - g_object_ref (ttext); - - start_index = offset_real (utf8, priv->position); - end_index = offset_real (utf8, priv->selection_bound); + start_index = offset_real (priv->text, priv->position); + end_index = offset_real (priv->text, priv->selection_bound); if (end_index == start_index) return FALSE; @@ -575,154 +489,98 @@ clutter_text_truncate_selection (ClutterText *ttext, end_index = temp; } - clutter_text_delete_text (ttext, start_index, end_index); + clutter_text_delete_text (self, start_index, end_index); + priv->position = start_index; priv->selection_bound = start_index; + return TRUE; } - -void -clutter_text_insert_unichar (ClutterText *ttext, - gunichar wc) +static gboolean +clutter_text_truncate_selection (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { - ClutterTextPrivate *priv; - GString *new = NULL; - const gchar *old_text; - glong pos; - - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); - g_return_if_fail (g_unichar_validate (wc)); - - if (wc == 0) - return; - - clutter_text_truncate_selection (ttext, NULL, 0); - - priv = ttext->priv; - - g_object_ref (ttext); - - old_text = clutter_text_get_text (ttext); - - - new = g_string_new (old_text); - pos = offset_to_bytes (old_text, priv->position); - new = g_string_insert_unichar (new, pos, wc); - - clutter_text_set_text (ttext, new->str); - - if (priv->position >= 0) - { - clutter_text_set_cursor_position (ttext, priv->position + 1); - clutter_text_set_selection_bound (ttext, priv->position); - } - - g_string_free (new, TRUE); - - g_object_unref (ttext); - - g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); -} - -void -clutter_text_delete_text (ClutterText *ttext, - gssize start_pos, - gssize end_pos) -{ - ClutterTextPrivate *priv; - GString *new = NULL; - gint start_bytes; - gint end_bytes; - const gchar *text; - - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); - - priv = ttext->priv; - text = clutter_text_get_text (ttext); - - if (end_pos == -1) - { - start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); - end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); - } - else - { - start_bytes = offset_to_bytes (text, start_pos); - end_bytes = offset_to_bytes (text, end_pos); - } - - new = g_string_new (text); - - new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - - clutter_text_set_text (ttext, new->str); - - g_string_free (new, TRUE); - g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); -} - -static void -clutter_text_finalize (GObject *gobject) -{ - ClutterTextPrivate *priv; - ClutterText *ttext; - GList *iter; - - ttext = CLUTTER_TEXT (gobject); - priv = ttext->priv; - - clutter_text_mappings_clear (ttext); - - for (iter = priv->commands; iter; iter=iter->next) - g_free (iter->data); - g_list_free (priv->commands); - priv->commands = NULL; + return clutter_text_truncate_selection_internal (ttext); } static void clutter_text_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec) + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - ClutterTextPrivate *priv; - ClutterText *ttext; - - ttext = CLUTTER_TEXT (gobject); - priv = ttext->priv; + ClutterText *self = CLUTTER_TEXT (gobject); switch (prop_id) { case PROP_TEXT: - clutter_text_set_text (ttext, g_value_get_string (value)); + clutter_text_set_text (self, g_value_get_string (value)); break; + case PROP_COLOR: - clutter_text_set_color (ttext, clutter_value_get_color (value)); + clutter_text_set_color (self, clutter_value_get_color (value)); break; + case PROP_FONT_NAME: - clutter_text_set_font_name (ttext, g_value_get_string (value)); + clutter_text_set_font_name (self, g_value_get_string (value)); break; + + case PROP_USE_MARKUP: + clutter_text_set_use_markup (self, g_value_get_boolean (value)); + break; + + case PROP_ATTRIBUTES: + clutter_text_set_attributes (self, g_value_get_boxed (value)); + break; + + case PROP_ALIGNMENT: + clutter_text_set_alignment (self, g_value_get_enum (value)); + break; + + case PROP_LINE_WRAP: + clutter_text_set_line_wrap (self, g_value_get_boolean (value)); + break; + + case PROP_LINE_WRAP_MODE: + clutter_text_set_line_wrap_mode (self, g_value_get_enum (value)); + break; + + case PROP_JUSTIFY: + clutter_text_set_justify (self, g_value_get_boolean (value)); + break; + + case PROP_ELLIPSIZE: + clutter_text_set_ellipsize (self, g_value_get_enum (value)); + break; + case PROP_POSITION: - clutter_text_set_cursor_position (ttext, g_value_get_int (value)); + clutter_text_set_cursor_position (self, g_value_get_int (value)); break; + case PROP_SELECTION_BOUND: - clutter_text_set_selection_bound (ttext, g_value_get_int (value)); + clutter_text_set_selection_bound (self, g_value_get_int (value)); break; + case PROP_CURSOR_VISIBLE: - clutter_text_set_cursor_visible (ttext, g_value_get_boolean (value)); + clutter_text_set_cursor_visible (self, g_value_get_boolean (value)); break; + case PROP_CURSOR_COLOR: - clutter_text_set_cursor_color (ttext, g_value_get_boxed (value)); + clutter_text_set_cursor_color (self, g_value_get_boxed (value)); break; + case PROP_EDITABLE: - clutter_text_set_editable (ttext, g_value_get_boolean (value)); + clutter_text_set_editable (self, g_value_get_boolean (value)); break; + case PROP_ACTIVATABLE: - clutter_text_set_activatable (ttext, g_value_get_boolean (value)); + clutter_text_set_activatable (self, g_value_get_boolean (value)); break; + case PROP_SELECTABLE: - clutter_text_set_selectable (ttext, g_value_get_boolean (value)); + clutter_text_set_selectable (self, g_value_get_boolean (value)); break; default: @@ -737,48 +595,87 @@ clutter_text_get_property (GObject *gobject, GValue *value, GParamSpec *pspec) { - ClutterTextPrivate *priv; - - priv = CLUTTER_TEXT (gobject)->priv; + ClutterTextPrivate *priv = CLUTTER_TEXT (gobject)->priv; switch (prop_id) { case PROP_TEXT: g_value_set_string (value, priv->text); break; + case PROP_FONT_NAME: g_value_set_string (value, priv->font_name); break; + case PROP_COLOR: clutter_value_set_color (value, &priv->text_color); break; + case PROP_CURSOR_VISIBLE: g_value_set_boolean (value, priv->cursor_visible); break; + case PROP_CURSOR_COLOR: clutter_value_set_color (value, &priv->cursor_color); break; + case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); break; + case PROP_SELECTION_BOUND: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->selection_bound)); break; + case PROP_EDITABLE: g_value_set_boolean (value, priv->editable); break; + case PROP_SELECTABLE: g_value_set_boolean (value, priv->selectable); break; + case PROP_ACTIVATABLE: g_value_set_boolean (value, priv->activatable); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } +static void +clutter_text_dispose (GObject *gobject) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + + /* get rid of the entire cache */ + clutter_text_dirty_cache (self); + + G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject); +} + +static void +clutter_text_finalize (GObject *gobject) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + ClutterTextPrivate *priv = self->priv; + + if (priv->font_desc) + pango_font_description_free (priv->font_desc); + + g_free (priv->text); + g_free (priv->font_name); + + clutter_text_mappings_clear (self); + + g_list_foreach (priv->commands, (GFunc) g_free, NULL); + g_list_free (priv->commands); + + G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject); +} + static void cursor_paint (ClutterText *ttext) { @@ -875,8 +772,8 @@ cursor_paint (ClutterText *ttext) static gboolean -clutter_text_press (ClutterActor *actor, - ClutterButtonEvent *bev) +clutter_text_button_press (ClutterActor *actor, + ClutterButtonEvent *bev) { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; @@ -899,14 +796,7 @@ clutter_text_press (ClutterActor *actor, ); /* we'll steal keyfocus if we do not have it */ - { - ClutterActor *stage; - for (stage = actor; - clutter_actor_get_parent (stage); - stage = clutter_actor_get_parent (stage)); - if (stage && CLUTTER_IS_STAGE (stage)) - clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); - } + clutter_actor_grab_key_focus (actor); priv->in_select_drag = TRUE; clutter_grab_pointer (actor); @@ -917,7 +807,7 @@ clutter_text_press (ClutterActor *actor, static gboolean clutter_text_motion (ClutterActor *actor, - ClutterMotionEvent *mev) + ClutterMotionEvent *mev) { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; @@ -926,9 +816,7 @@ clutter_text_motion (ClutterActor *actor, const gchar *text; if (!priv->in_select_drag) - { - return FALSE; - } + return FALSE; text = clutter_text_get_text (ttext); @@ -941,9 +829,7 @@ clutter_text_motion (ClutterActor *actor, CLUTTER_UNITS_TO_INT (y)); if (priv->selectable) - { - clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); - } + clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); else { clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); @@ -954,8 +840,8 @@ clutter_text_motion (ClutterActor *actor, } static gboolean -clutter_text_release (ClutterActor *actor, - ClutterButtonEvent *bev) +clutter_text_button_release (ClutterActor *actor, + ClutterButtonEvent *bev) { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; @@ -968,6 +854,54 @@ clutter_text_release (ClutterActor *actor, return FALSE; } +static gboolean +clutter_text_key_press (ClutterActor *actor, + ClutterKeyEvent *kev) +{ + ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + gint keyval = clutter_key_event_symbol (kev); + GList *iter; + + if (!priv->editable) + return FALSE; + + for (iter = priv->mappings; iter != NULL; iter = iter->next) + { + ClutterTextMapping *mapping = iter->data; + + if ( + (mapping->keyval == keyval) && + ( + (mapping->state == 0) || + (mapping->state && (kev->modifier_state & mapping->state)) + ) + ) + { + if (!g_str_equal (mapping->action, "activate") || + priv->activatable) + return clutter_text_action (CLUTTER_TEXT (actor), + mapping->action, + (ClutterEvent *) kev); + } + } + + { + gunichar key_unichar = clutter_key_event_unicode (kev); + + if (key_unichar == '\r') /* return is reported as CR we want LF */ + key_unichar = '\n'; + + if (g_unichar_validate (key_unichar)) + { + clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + return TRUE; + } + } + + return FALSE; +} + + static void clutter_text_paint (ClutterActor *self) { @@ -1002,9 +936,9 @@ clutter_text_paint (ClutterActor *self) static void clutter_text_get_preferred_width (ClutterActor *self, - ClutterUnit for_height, - ClutterUnit *min_width_p, - ClutterUnit *natural_width_p) + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p) { ClutterText *text = CLUTTER_TEXT (self); ClutterTextPrivate *priv = text->priv; @@ -1034,9 +968,9 @@ clutter_text_get_preferred_width (ClutterActor *self, static void clutter_text_get_preferred_height (ClutterActor *self, - ClutterUnit for_width, - ClutterUnit *min_height_p, - ClutterUnit *natural_height_p) + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p) { ClutterText *text = CLUTTER_TEXT (self); @@ -1069,8 +1003,8 @@ clutter_text_get_preferred_height (ClutterActor *self, static void clutter_text_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean origin_changed) + const ClutterActorBox *box, + gboolean origin_changed) { ClutterText *text = CLUTTER_TEXT (self); ClutterActorClass *parent_class; @@ -1083,13 +1017,6 @@ clutter_text_allocate (ClutterActor *self, parent_class->allocate (self, box, origin_changed); } -static void -clutter_text_constructed (GObject *object) -{ - if (G_OBJECT_CLASS (clutter_text_parent_class)->constructed != NULL) - G_OBJECT_CLASS (clutter_text_parent_class)->constructed (object); -} - static void clutter_text_class_init (ClutterTextClass *klass) { @@ -1099,7 +1026,7 @@ clutter_text_class_init (ClutterTextClass *klass) gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; - gobject_class->constructed = clutter_text_constructed; + gobject_class->dispose = clutter_text_dispose; gobject_class->finalize = clutter_text_finalize; actor_class->paint = clutter_text_paint; @@ -1107,8 +1034,8 @@ 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->button_press_event = clutter_text_press; - actor_class->button_release_event = clutter_text_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; /** @@ -1145,40 +1072,37 @@ clutter_text_class_init (ClutterTextClass *klass) * * Whether key events delivered to the actor causes editing. */ - g_object_class_install_property - (gobject_class, PROP_EDITABLE, - g_param_spec_boolean ("editable", - "Editable", - "Whether the text is editable", - TRUE, - G_PARAM_READWRITE)); - + pspec = g_param_spec_boolean ("editable", + "Editable", + "Whether the text is editable", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec); /** * ClutterText:selectable: * * Whether it is possible to select text. */ - g_object_class_install_property - (gobject_class, PROP_SELECTABLE, - g_param_spec_boolean ("selectable", - "Editable", - "Whether the text is selectable", - TRUE, - G_PARAM_READWRITE)); + pspec = g_param_spec_boolean ("selectable", + "Selectable", + "Whether the text is selectable", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec); /** * ClutterText:activatable: * * Toggles whether return invokes the activate signal or not. */ - g_object_class_install_property - (gobject_class, PROP_ACTIVATABLE, - g_param_spec_boolean ("activatable", - "Editable", - "Whether return causes the activate signal to be fired", - TRUE, - G_PARAM_READWRITE)); + pspec = g_param_spec_boolean ("activatable", + "Activatable", + "Whether pressing return causes the " + "activate signal to be emitted", + TRUE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec); /** * ClutterText:cursor-visible: @@ -1209,29 +1133,123 @@ clutter_text_class_init (ClutterTextClass *klass) * * The current input cursor position. -1 is taken to be the end of the text */ - g_object_class_install_property - (gobject_class, PROP_POSITION, - g_param_spec_int ("position", + pspec = g_param_spec_int ("position", "Position", "The cursor position", -1, G_MAXINT, -1, - G_PARAM_READWRITE)); - + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_POSITION, pspec); /** * ClutterText:selection-bound: * * The current input cursor position. -1 is taken to be the end of the text */ - g_object_class_install_property - (gobject_class, PROP_SELECTION_BOUND, - g_param_spec_int ("selection-bound", + pspec = g_param_spec_int ("selection-bound", "Selection-bound", "The cursor position of the other end of the selection.", -1, G_MAXINT, -1, - G_PARAM_READWRITE)); + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); + + pspec = g_param_spec_boxed ("attributes", + "Attributes", + "A list of style attributes to apply to " + "the contents of the actor", + PANGO_TYPE_ATTR_LIST, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec); + + /** + * ClutterText:use-markup: + * + * Whether the text includes Pango markup. See pango_layout_set_markup() + * in the Pango documentation. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("use-markup", + "Use markup", + "Whether or not the text " + "includes Pango markup", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); + + /** + * ClutterText:line-wrap: + * + * Whether to wrap the lines of #ClutterText:text if the contents + * exceed the available allocation. The wrapping strategy is + * controlled by the #ClutterText:line-wrap-mode property. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("line-wrap", + "Line wrap", + "If set, wrap the lines if the text " + "becomes too wide", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec); + + /** + * ClutterText:line-wrap-mode: + * + * If #ClutterText:line-wrap is set to %TRUE, this property will + * control how the text is wrapped. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("line-wrap-mode", + "Line wrap mode", + "Control how line-wrapping is done", + PANGO_TYPE_WRAP_MODE, + PANGO_WRAP_WORD, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec); + + pspec = g_param_spec_enum ("ellipsize", + "Ellipsize", + "The preferred place to ellipsize the string", + PANGO_TYPE_ELLIPSIZE_MODE, + PANGO_ELLIPSIZE_NONE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec); + + /** + * ClutterText:alignment: + * + * The preferred alignment for the text. This property controls + * the alignment of multi-line paragraphs. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("alignment", + "Alignment", + "The preferred alignment for the string, " + "for multi-line text", + PANGO_TYPE_ALIGNMENT, + PANGO_ALIGN_LEFT, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ALIGNMENT, pspec); + + /** + * ClutterText:justify: + * + * Whether the contents of the #ClutterText should be justified + * on both margins. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("justify", + "Justify", + "Whether the text should be justified", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); /** * ClutterText::text-changed: @@ -1248,7 +1266,6 @@ clutter_text_class_init (ClutterTextClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - text_signals[CURSOR_EVENT] = g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), @@ -1259,15 +1276,14 @@ clutter_text_class_init (ClutterTextClass *klass) G_TYPE_NONE, 1, CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); - - /** + /** * ClutterText::activate * @actor: the actor which received the event * * The ::activate signal is emitted each time the entry is 'activated' * by the user, normally by pressing the 'Enter' key. * - * Since: 0.4 + * Since: 1.0 */ text_signals[ACTIVATE] = g_signal_new ("activate", @@ -1278,7 +1294,6 @@ clutter_text_class_init (ClutterTextClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); } @@ -1440,17 +1455,6 @@ clutter_text_get_cursor_color (ClutterText *text, *color = priv->cursor_color; } - -gint -clutter_text_get_selection_bound (ClutterText *text) -{ - ClutterTextPrivate *priv; - - priv = text->priv; - - return priv->selection_bound; -} - gchar * clutter_text_get_selection (ClutterText *text) { @@ -1487,20 +1491,34 @@ clutter_text_get_selection (ClutterText *text) return str; } - - void -clutter_text_set_selection_bound (ClutterText *text, - gint selection_bound) +clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound) { ClutterTextPrivate *priv; - priv = text->priv; - priv->selection_bound = selection_bound; + g_return_if_fail (CLUTTER_IS_TEXT (self)); - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + priv = self->priv; + + if (priv->selection_bound != selection_bound) + { + priv->selection_bound = selection_bound; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "selection-bound"); + } } +gint +clutter_text_get_selection_bound (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); + + return self->priv->selection_bound; +} /****************************************************************/ /* The following are the commands available for keybinding when */ @@ -1843,7 +1861,6 @@ clutter_text_action_delete_previous (ClutterText *ttext, return TRUE; } - static void init_commands (ClutterText *ttext) { ClutterTextPrivate *priv = ttext->priv; @@ -1897,49 +1914,6 @@ clutter_text_action (ClutterText *ttext, return FALSE; } -static gboolean -clutter_text_key_press (ClutterActor *actor, - ClutterKeyEvent *kev) -{ - ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; - gint keyval = clutter_key_event_symbol (kev); - GList *iter; - - if (!priv->editable) - return FALSE; - - for (iter=priv->mappings;iter;iter=iter->next) - { - ClutterTextMapping *mapping = iter->data; - - if ( - (mapping->keyval == keyval) && - ( - (mapping->state == 0) || - (mapping->state && (kev->modifier_state & mapping->state)) - ) - ) - { - if (!g_str_equal (mapping->action, "activate") || - priv->activatable) - return clutter_text_action (CLUTTER_TEXT (actor), mapping->action, (ClutterEvent*)kev); - } - } - - { - gunichar key_unichar = clutter_key_event_unicode (kev); - - if (key_unichar == '\r') /* return is reported as CR we want LF */ - key_unichar = '\n'; - if (g_unichar_validate (key_unichar)) - { - clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); - return TRUE; - } - } - return FALSE; -} - G_CONST_RETURN gchar * clutter_text_get_font_name (ClutterText *text) { @@ -2063,30 +2037,516 @@ clutter_text_get_color (ClutterText *text, *color = priv->text_color; } -gboolean -clutter_text_get_line_wrap (ClutterText *text) +/** + * clutter_text_set_ellipsize: + * @self: a #ClutterText + * @mode: a #PangoEllipsizeMode + * + * Sets the mode used to ellipsize (add an ellipsis: "...") to the + * text if there is not enough space to render the entire contents + * of a #ClutterText actor + * + * Since: 1.0 + */ +void +clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode) { - g_return_val_if_fail (CLUTTER_IS_TEXT (text), FALSE); + ClutterTextPrivate *priv; - return text->priv->wrap; + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && + mode <= PANGO_ELLIPSIZE_END); + + priv = self->priv; + + if ((PangoEllipsizeMode) priv->ellipsize != mode) + { + priv->ellipsize = mode; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "ellipsize"); + } +} + +/** + * clutter_text_get_ellipsize: + * @self: a #ClutterText + * + * Returns the ellipsizing position of a #ClutterText actor, as + * set by clutter_text_set_ellipsize(). + * + * Return value: #PangoEllipsizeMode + * + * Since: 1.0 + */ +PangoEllipsizeMode +clutter_text_get_ellipsize (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE); + + return self->priv->ellipsize; +} + +gboolean +clutter_text_get_line_wrap (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->wrap; } void -clutter_text_set_line_wrap (ClutterText *text, +clutter_text_set_line_wrap (ClutterText *self, gboolean line_wrap) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = text->priv; + priv = self->priv; if (priv->wrap != line_wrap) { priv->wrap = line_wrap; - clutter_text_dirty_cache (text); + clutter_text_dirty_cache (self); - clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "line-wrap"); } } + +/** + * clutter_text_set_line_wrap_mode: + * @self: a #ClutterText + * @wrap_mode: the line wrapping mode + * + * If line wrapping is enabled (see clutter_text_set_line_wrap()) this + * function controls how the line wrapping is performed. The default is + * %PANGO_WRAP_WORD which means wrap on word boundaries. + * + * Since: 1.0 + */ +void +clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->wrap_mode != wrap_mode) + { + priv->wrap_mode = wrap_mode; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "line-wrap-mode"); + } +} + +/** + * clutter_text_get_line_wrap_mode: + * @self: a #ClutterText + * + * Retrieves the line wrap mode used by the #ClutterText actor. + * + * See clutter_text_set_line_wrap_mode (). + * + * Return value: the wrap mode used by the #ClutterText + * + * Since: 1.0 + */ +PangoWrapMode +clutter_text_get_line_wrap_mode (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD); + + return self->priv->wrap_mode; +} + +/** + * clutter_text_set_attributes: + * @self: a #ClutterText + * @attrs: a #PangoAttrList or %NULL to unset the attributes + * + * Sets the attributes list that are going to be applied to the + * #ClutterText contents. The attributes set with this function + * will be ignored if the #ClutterText:use_markup property is + * set to %TRUE. + * + * The #ClutterText actor will take a reference on the #PangoAttrList + * passed to this function. + * + * Since: 1.0 + */ +void +clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (attrs) + pango_attr_list_ref (attrs); + + if (priv->attrs) + pango_attr_list_unref (priv->attrs); + + if (!priv->use_markup) + { + if (attrs) + pango_attr_list_ref (attrs); + + if (priv->effective_attrs) + pango_attr_list_unref (priv->effective_attrs); + + priv->effective_attrs = attrs; + } + + priv->attrs = attrs; + + clutter_text_dirty_cache (self); + + g_object_notify (G_OBJECT (self), "attributes"); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); +} + +/** + * clutter_text_get_attributes: + * @self: a #ClutterText + * + * Gets the attribute list that was set on the #ClutterText actor + * clutter_text_set_attributes(), if any. + * + * Return value: the attribute list, or %NULL if none was set. The + * returned value is owned by the #ClutterText and should not be + * unreferenced. + * + * Since: 1.0 + */ +PangoAttrList * +clutter_text_get_attributes (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + return self->priv->attrs; +} + +/** + * clutter_text_set_alignment: + * @self: a #ClutterText + * @alignment: A #PangoAlignment + * + * Sets text alignment of the #ClutterText actor. + * + * The alignment will only be used when the contents of the + * #ClutterText actor are enough to wrap, and the #ClutterText:line-wrap + * property is set to %TRUE. + * + * Since: 1.0 + */ +void +clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->alignment != alignment) + { + priv->alignment = alignment; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "alignment"); + } +} + +/** + * clutter_text_get_alignment: + * @self: a #ClutterText + * + * Retrieves the alignment of @self. + * + * Return value: a #PangoAlignment + * + * Since 1.0 + */ +PangoAlignment +clutter_text_get_alignment (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT); + + return self->priv->alignment; +} + +/** + * clutter_text_set_use_markup: + * @self: a #ClutterText + * @setting: %TRUE if the text should be parsed for markup. + * + * Sets whether the contents of the #ClutterText actor contains markup + * in Pango's text markup language. + * + * Since: 1.0 + */ +void +clutter_text_set_use_markup (ClutterText *self, + gboolean setting) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->use_markup != setting) + { + priv->use_markup = setting; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "use-markup"); + } +} + +/** + * clutter_text_get_use_markup: + * @self: a #ClutterText + * + * Retrieves whether the contents of the #ClutterText actor should be + * parsed for the Pango text markup. + * + * Return value: %TRUE if the contents will be parsed for markup + * + * Since: 1.0 + */ +gboolean +clutter_text_get_use_markup (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->use_markup; +} + +/** + * clutter_text_set_justify: + * @self: a #ClutterText + * @justify: whether the text should be justified + * + * Sets whether the text of the #ClutterText actor should be justified + * on both margins. This setting is ignored if Clutter is compiled + * against Pango < 1.18. + * + * Since: 0.6 + */ +void +clutter_text_set_justify (ClutterText *self, + gboolean justify) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->justify != justify) + { + priv->justify = justify; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "justify"); + } +} + +/** + * clutter_text_get_justify: + * @self: a #ClutterText + * + * Retrieves whether the #ClutterText actor should justify its contents + * on both margins. + * + * Return value: %TRUE if the text should be justified + * + * Since: 0.6 + */ +gboolean +clutter_text_get_justify (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->justify; +} + +/** + * clutter_text_get_cursor_position: + * @self: a #ClutterText + * + * Retrieves the cursor position. + * + * Return value: the cursor position, in characters + * + * Since: 1.0 + */ +gint +clutter_text_get_cursor_position (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); + + return self->priv->position; +} + +void +clutter_text_set_cursor_position (ClutterText *self, + gint position) +{ + ClutterTextPrivate *priv; + gint len; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->text == NULL) + return; + + len = g_utf8_strlen (priv->text, -1); + + if (position < 0 || position >= len) + priv->position = -1; + else + priv->position = position; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); +} + +void +clutter_text_insert_unichar (ClutterText *self, + gunichar wc) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + glong pos; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (g_unichar_validate (wc)); + + if (wc == 0) + return; + + clutter_text_truncate_selection (self, NULL, 0); + + priv = self->priv; + + g_object_ref (self); + + new = g_string_new (priv->text); + pos = offset_to_bytes (priv->text, priv->position); + new = g_string_insert_unichar (new, pos, wc); + + clutter_text_set_text (self, new->str); + + if (priv->position >= 0) + { + clutter_text_set_cursor_position (self, priv->position + 1); + clutter_text_set_selection_bound (self, priv->position); + } + + g_string_free (new, TRUE); + + g_object_unref (self); + + g_signal_emit (G_OBJECT (self), text_signals[TEXT_CHANGED], 0); +} + +void +clutter_text_delete_text (ClutterText *ttext, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint start_bytes; + gint end_bytes; + const gchar *text; + + g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + + priv = ttext->priv; + text = clutter_text_get_text (ttext); + + if (end_pos == -1) + { + start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); + end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); + } + else + { + start_bytes = offset_to_bytes (text, start_pos); + end_bytes = offset_to_bytes (text, end_pos); + } + + new = g_string_new (text); + + new = g_string_erase (new, start_bytes, end_bytes - start_bytes); + + clutter_text_set_text (ttext, new->str); + + g_string_free (new, TRUE); + g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); +} + + +void +clutter_text_add_mapping (ClutterText *ttext, + guint keyval, + ClutterModifierType state, + const gchar *commandline) +{ + ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); + ClutterTextPrivate *priv = ttext->priv; + tmapping->keyval = keyval; + tmapping->state = state; + tmapping->action = commandline; + priv->mappings = g_list_append (priv->mappings, tmapping); +} + +void +clutter_text_add_action (ClutterText *ttext, + const gchar *name, + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event)) +{ + TextCommand *tcommand = g_new (TextCommand, 1); + ClutterTextPrivate *priv = ttext->priv; + tcommand->name = name; + tcommand->func = func; + priv->commands = g_list_append (priv->commands, tcommand); +} + From 97cf7e68199f9ac901777f7c68b5475373d5a4eb Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:35:52 +0000 Subject: [PATCH 011/108] Correctly initialize the initial state of ClutterText Some of the values in ClutterText have an initial state that does not match a zero-ed out ClutterTextPrivate structure. --- clutter/clutter-text.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4f2e3d7d6..de3d7c956 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -122,7 +122,7 @@ struct _ClutterTextPrivate /* the x position in the pangolayout, used to * avoid drifting when repeatedly moving up|down */ - gint x_pos; + gint x_pos; /* Where to draw the cursor */ ClutterGeometry cursor_pos; @@ -244,9 +244,6 @@ clutter_text_create_layout_no_cache (ClutterText *text, ClutterTextPrivate *priv = text->priv; PangoLayout *layout; - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - layout = pango_layout_new (_context); pango_layout_set_font_description (layout, priv->font_desc); @@ -1024,6 +1021,8 @@ clutter_text_class_init (ClutterTextClass *klass) ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; + _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; gobject_class->dispose = clutter_text_dispose; @@ -1301,9 +1300,29 @@ static void clutter_text_init (ClutterText *self) { ClutterTextPrivate *priv; + int i; self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); + priv->alignment = PANGO_ALIGN_LEFT; + priv->wrap = FALSE; + priv->wrap_mode = PANGO_WRAP_WORD; + priv->ellipsize = PANGO_ELLIPSIZE_NONE; + priv->use_underline = FALSE; + priv->use_markup = FALSE; + priv->justify = FALSE; + + for (i = 0; i < N_CACHED_LAYOUTS; i++) + priv->cached_layouts[i].layout = NULL; + + priv->text = NULL; + + priv->text_color = default_text_color; + priv->cursor_color = default_cursor_color; + + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + priv->font_desc = pango_font_description_from_string (priv->font_name); + priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; From 64af7bafe387571d7087dd4c99b4f459c1ca41c0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:37:46 +0000 Subject: [PATCH 012/108] Move test-threads to ClutterText The test-threads interactive test is a good candidate for the switch from ClutterLabel to ClutterText to verify that the behaviour of the two classes is the same. --- tests/interactive/test-threads.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/interactive/test-threads.c b/tests/interactive/test-threads.c index 86eb6cfe3..5f946b653 100644 --- a/tests/interactive/test-threads.c +++ b/tests/interactive/test-threads.c @@ -42,7 +42,7 @@ test_thread_done_idle (gpointer user_data) g_print ("Thread completed\n"); - clutter_label_set_text (CLUTTER_LABEL (data->label), "Completed"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Completed"); clutter_timeline_stop (data->timeline); test_thread_data_free (data); @@ -67,7 +67,7 @@ update_label_idle (gpointer data) text = g_strdup_printf ("Count to %d", update->count); - clutter_label_set_text (CLUTTER_LABEL (update->thread_data->label), text); + clutter_text_set_text (CLUTTER_TEXT (update->thread_data->label), text); clutter_actor_set_width (update->thread_data->label, -1); if (update->count == 0) @@ -151,7 +151,7 @@ on_key_press_event (ClutterStage *stage, switch (clutter_key_event_symbol (event)) { case CLUTTER_s: - clutter_label_set_text (CLUTTER_LABEL (help_label), "Press 'q' to quit"); + clutter_text_set_text (CLUTTER_TEXT (help_label), "Press 'q' to quit"); clutter_timeline_start (timeline); @@ -191,10 +191,10 @@ test_threads_main (int argc, char *argv[]) clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); clutter_actor_set_size (stage, 600, 300); - count_label = clutter_label_new_with_text ("Mono 12", "Counter"); + count_label = clutter_text_new_with_text ("Mono 12", "Counter"); clutter_actor_set_position (count_label, 350, 50); - help_label = clutter_label_new_with_text ("Mono 12", "Press 's' to start"); + help_label = clutter_text_new_with_text ("Mono 12", "Press 's' to start"); clutter_actor_set_position (help_label, 50, 50); rect = clutter_rectangle_new_with_color (&rect_color); From d5df1bebcf28cb035ea9529fbc1352c8300f7e25 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:40:55 +0000 Subject: [PATCH 013/108] Add text-visibility accessors for ClutterText A ClutterText can be put in "password mode" by setting the text as "invisible": every character inside the Text actor's contents will be replaced when building the Pango layout with a specific Unicode character. The Unicode character is set to '*' by default, but the user can be changed using the provided API. --- clutter/clutter-text.c | 652 ++++++++++++++++++++++++++++++----------- clutter/clutter-text.h | 21 +- 2 files changed, 494 insertions(+), 179 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index de3d7c956..ffee909d9 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -3,9 +3,10 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2006-2008 OpenedHand + * Copyright (C) 2008 Intel Corporation. * - * Authored By Øyvind Kolås + * Authored By: Øyvind Kolås + * Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,9 +19,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* TODO: undo/redo hooks? @@ -112,6 +111,7 @@ struct _ClutterTextPrivate guint selectable : 1; guint in_select_drag : 1; guint cursor_color_set : 1; + guint text_visible : 1; /* current cursor position */ gint position; @@ -124,17 +124,25 @@ struct _ClutterTextPrivate */ gint x_pos; + /* the length of the text, in bytes */ + gint n_bytes; + + /* the length of the text, in characters */ + gint n_chars; + /* Where to draw the cursor */ ClutterGeometry cursor_pos; ClutterColor cursor_color; - ClutterColor selection_color; - GList *mappings; GList *commands; /* each instance has it's own set of commands so that actor specific actions can be added to single actor classes */ + + gint max_length; + + gunichar priv_char; }; enum @@ -158,7 +166,9 @@ enum PROP_CURSOR_COLOR_SET, PROP_EDITABLE, PROP_SELECTABLE, - PROP_ACTIVATABLE + PROP_ACTIVATABLE, + PROP_TEXT_VISIBLE, + PROP_INVISIBLE_CHAR }; enum @@ -258,7 +268,36 @@ clutter_text_create_layout_no_cache (ClutterText *text, if (priv->text) { if (!priv->use_markup) - pango_layout_set_text (layout, priv->text, -1); + { + if (priv->text_visible) + pango_layout_set_text (layout, priv->text, priv->n_bytes); + else + { + GString *str = g_string_sized_new (priv->n_bytes); + gunichar invisible_char; + gchar buf[7]; + gint char_len, i; + + if (priv->priv_char != 0) + invisible_char = priv->priv_char; + else + invisible_char = ' '; + + /* we need to convert the string built of invisible + * characters into UTF-8 for it to be fed to the Pango + * layout + */ + memset (buf, 0, sizeof (buf)); + char_len = g_unichar_to_utf8 (invisible_char, buf); + + for (i = 0; i < priv->n_chars; i++) + g_string_append_len (str, buf, char_len); + + pango_layout_set_text (layout, str->str, str->len); + + g_string_free (str, TRUE); + } + } else pango_layout_set_markup (layout, priv->text, -1); } @@ -423,20 +462,16 @@ clutter_text_position_to_coords (ClutterText *ttext, priv = ttext->priv; if (position == -1) - { - index_ = strlen (text); - } + index_ = strlen (text); else - { - index_ = offset_to_bytes (text, position); - } + index_ = offset_to_bytes (text, position); if (index_ > strlen (text)) index_ = strlen (text); - pango_layout_get_cursor_pos ( - clutter_text_get_layout (ttext), - index_, &rect, NULL); + pango_layout_get_cursor_pos (clutter_text_get_layout (ttext), + index_, + &rect, NULL); if (x) *x = rect.x / PANGO_SCALE; @@ -580,17 +615,24 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_selectable (self, g_value_get_boolean (value)); break; + case PROP_TEXT_VISIBLE: + clutter_text_set_text_visible (self, g_value_get_boolean (value)); + break; + + case PROP_INVISIBLE_CHAR: + clutter_text_set_invisible_char (self, g_value_get_uint (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; } } static void clutter_text_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { ClutterTextPrivate *priv = CLUTTER_TEXT (gobject)->priv; @@ -636,9 +678,16 @@ clutter_text_get_property (GObject *gobject, g_value_set_boolean (value, priv->activatable); break; + case PROP_TEXT_VISIBLE: + g_value_set_boolean (value, priv->text_visible); + break; + + case PROP_INVISIBLE_CHAR: + g_value_set_uint (value, priv->priv_char); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; } } @@ -674,12 +723,11 @@ clutter_text_finalize (GObject *gobject) } static void -cursor_paint (ClutterText *ttext) +cursor_paint (ClutterText *self) { - ClutterTextPrivate *priv = ttext->priv; + ClutterTextPrivate *priv = self->priv; - if (priv->editable && - priv->cursor_visible) + if (priv->editable && priv->cursor_visible) { if (priv->cursor_color_set) { @@ -690,15 +738,13 @@ cursor_paint (ClutterText *ttext) } else { - ClutterColor color; - clutter_text_get_color (ttext, &color); - cogl_set_source_color4ub (color.red, - color.green, - color.blue, - color.alpha); + cogl_set_source_color4ub (priv->text_color.red, + priv->text_color.green, + priv->text_color.blue, + priv->text_color.alpha); } - clutter_text_ensure_cursor_position (ttext); + clutter_text_ensure_cursor_position (self); if (priv->position == 0) priv->cursor_pos.x -= 2; @@ -712,10 +758,12 @@ cursor_paint (ClutterText *ttext) } else { + PangoLayout *layout = clutter_text_get_layout (self); + const gchar *utf8 = priv->text; gint lines; gint start_index; gint end_index; - const gchar *utf8 = clutter_text_get_text (ttext); + gint line_no; start_index = offset_to_bytes (utf8, priv->position); end_index = offset_to_bytes (utf8, priv->selection_bound); @@ -727,9 +775,8 @@ cursor_paint (ClutterText *ttext) end_index = temp; } - PangoLayout *layout = clutter_text_get_layout (ttext); lines = pango_layout_get_line_count (layout); - gint line_no; + for (line_no = 0; line_no < lines; line_no++) { PangoLayoutLine *line; @@ -746,23 +793,26 @@ cursor_paint (ClutterText *ttext) if (maxindex < start_index) continue; - pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); + pango_layout_line_get_x_ranges (line, start_index, end_index, + &ranges, + &n_ranges); pango_layout_line_x_to_index (line, 0, &index, NULL); - clutter_text_position_to_coords (ttext, bytes_to_offset (utf8, index), NULL, &y, &height); + clutter_text_position_to_coords (self, + bytes_to_offset (utf8, index), + NULL, &y, &height); - for (i=0;ipriv; - ClutterUnit x, y; - gint index_; - const gchar *text; - - text = clutter_text_get_text (ttext); + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; + ClutterUnit x, y; + gint index_; x = CLUTTER_UNITS_FROM_INT (bev->x); y = CLUTTER_UNITS_FROM_INT (bev->y); clutter_actor_transform_stage_point (actor, x, y, &x, &y); - index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), - CLUTTER_UNITS_TO_INT (y)); + index_ = clutter_text_coords_to_position (self, + CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); - clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); - clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_) - ); + clutter_text_set_cursor_position (self, bytes_to_offset (priv->text, index_)); + clutter_text_set_selection_bound (self, bytes_to_offset (priv->text, index_)); + + /* grab the pointer */ + priv->in_select_drag = TRUE; + clutter_grab_pointer (actor); /* we'll steal keyfocus if we do not have it */ clutter_actor_grab_key_focus (actor); - priv->in_select_drag = TRUE; - clutter_grab_pointer (actor); - return TRUE; } @@ -842,12 +890,15 @@ clutter_text_button_release (ClutterActor *actor, { ClutterText *ttext = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = ttext->priv; + if (priv->in_select_drag) { clutter_ungrab_pointer (); priv->in_select_drag = FALSE; + return TRUE; } + return FALSE; } @@ -898,7 +949,6 @@ clutter_text_key_press (ClutterActor *actor, return FALSE; } - static void clutter_text_paint (ClutterActor *self) { @@ -1006,8 +1056,9 @@ clutter_text_allocate (ClutterActor *self, ClutterText *text = CLUTTER_TEXT (self); ClutterActorClass *parent_class; - /* Ensure that there is a cached layout with the right width so that - we don't need to create the text during the paint run */ + /* Ensure that there is a cached layout with the right width so + * that we don't need to create the text during the paint run + */ clutter_text_create_layout (text, box->x2 - box->x1); parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); @@ -1023,6 +1074,8 @@ clutter_text_class_init (ClutterTextClass *klass) _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); + gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; gobject_class->dispose = clutter_text_dispose; @@ -1043,7 +1096,7 @@ clutter_text_class_init (ClutterTextClass *klass) * The font to be used by the #ClutterText, as a string * that can be parsed by pango_font_description_from_string(). * - * Since: 0.2 + * Since: 1.0 */ pspec = g_param_spec_string ("font-name", "Font Name", @@ -1110,22 +1163,19 @@ clutter_text_class_init (ClutterTextClass *klass) * if both cursor-visible is set and editable is set at the same time, * the value defaults to TRUE. */ - g_object_class_install_property - (gobject_class, PROP_CURSOR_VISIBLE, - g_param_spec_boolean ("cursor-visible", - "Cursor Visible", - "Whether the input cursor is visible", - TRUE, - G_PARAM_READWRITE)); + pspec = g_param_spec_boolean ("cursor-visible", + "Cursor Visible", + "Whether the input cursor is visible", + TRUE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); - - g_object_class_install_property - (gobject_class, PROP_CURSOR_COLOR, - g_param_spec_boxed ("cursor-color", - "Cursor Colour", - "Cursor Colour", - CLUTTER_TYPE_COLOR, - G_PARAM_READWRITE)); + pspec = clutter_param_spec_color ("cursor-color", + "Cursor Colour", + "Cursor Colour", + &default_cursor_color, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); /** * ClutterText:position: @@ -1133,11 +1183,11 @@ clutter_text_class_init (ClutterTextClass *klass) * The current input cursor position. -1 is taken to be the end of the text */ pspec = g_param_spec_int ("position", - "Position", - "The cursor position", - -1, G_MAXINT, - -1, - G_PARAM_READWRITE); + "Position", + "The cursor position", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_POSITION, pspec); /** @@ -1146,11 +1196,12 @@ clutter_text_class_init (ClutterTextClass *klass) * The current input cursor position. -1 is taken to be the end of the text */ pspec = g_param_spec_int ("selection-bound", - "Selection-bound", - "The cursor position of the other end of the selection.", - -1, G_MAXINT, - -1, - G_PARAM_READWRITE); + "Selection-bound", + "The cursor position of the other end " + "of the selection", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); pspec = g_param_spec_boxed ("attributes", @@ -1250,11 +1301,30 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); - /** + pspec = g_param_spec_boolean ("text-visible", + "Text Visible", + "Whether the text should be visible " + "or subsituted with an invisible " + "Unicode character", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_TEXT_VISIBLE, pspec); + + pspec = g_param_spec_unichar ("invisible-char", + "Invisible Character", + "The Unicode character used when the " + "text is set as not visible", + '*', + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + + /** * ClutterText::text-changed: * @actor: the actor which received the event * - * The ::text-changed signal is emitted after @entry's text changes + * The ::text-changed signal is emitted after @actor's text changes + * + * Since: 1.0 */ text_signals[TEXT_CHANGED] = g_signal_new ("text-changed", @@ -1279,7 +1349,7 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText::activate * @actor: the actor which received the event * - * The ::activate signal is emitted each time the entry is 'activated' + * The ::activate signal is emitted each time the actor is 'activated' * by the user, normally by pressing the 'Enter' key. * * Since: 1.0 @@ -1292,8 +1362,6 @@ clutter_text_class_init (ClutterTextClass *klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); } static void @@ -1326,9 +1394,13 @@ clutter_text_init (ClutterText *self) priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; + priv->selectable = TRUE; priv->cursor_color_set = FALSE; + priv->text_visible = TRUE; + priv->priv_char = '*'; + init_commands (self); /* FIXME: free */ init_mappings (self); /* FIXME: free */ } @@ -1347,7 +1419,7 @@ clutter_text_new_full (const gchar *font_name, ClutterActor * clutter_text_new_with_text (const gchar *font_name, - const gchar *text) + const gchar *text) { return g_object_new (CLUTTER_TYPE_TEXT, "font-name", font_name, @@ -1355,98 +1427,133 @@ clutter_text_new_with_text (const gchar *font_name, NULL); } - void -clutter_text_set_editable (ClutterText *text, +clutter_text_set_editable (ClutterText *self, gboolean editable) { ClutterTextPrivate *priv; - priv = text->priv; - priv->editable = editable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->editable != editable) + { + priv->editable = editable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "editable"); + } } gboolean -clutter_text_get_editable (ClutterText *text) +clutter_text_get_editable (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->editable; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->editable; } void -clutter_text_set_selectable (ClutterText *text, - gboolean selectable) +clutter_text_set_selectable (ClutterText *self, + gboolean selectable) { ClutterTextPrivate *priv; - priv = text->priv; - priv->selectable = selectable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->selectable != selectable) + { + priv->selectable = selectable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "selectable"); + } } gboolean -clutter_text_get_selectable (ClutterText *text) +clutter_text_get_selectable (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->selectable; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->selectable; } void -clutter_text_set_activatable (ClutterText *text, - gboolean activatable) +clutter_text_set_activatable (ClutterText *self, + gboolean activatable) { ClutterTextPrivate *priv; - priv = text->priv; - priv->activatable = activatable; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->activatable != activatable) + { + priv->activatable = activatable; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "activatable"); + } } gboolean -clutter_text_get_activatable (ClutterText *text) +clutter_text_get_activatable (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->activatable; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->activatable; } - void -clutter_text_set_cursor_visible (ClutterText *text, - gboolean cursor_visible) +clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible) { ClutterTextPrivate *priv; - priv = text->priv; - priv->cursor_visible = cursor_visible; - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->cursor_visible != cursor_visible) + { + priv->cursor_visible = cursor_visible; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-visible"); + } } gboolean -clutter_text_get_cursor_visible (ClutterText *text) +clutter_text_get_cursor_visible (ClutterText *self) { - ClutterTextPrivate *priv; - priv = text->priv; - return priv->cursor_visible; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->cursor_visible; } void -clutter_text_set_cursor_color (ClutterText *text, - const ClutterColor *color) +clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); - g_return_if_fail (color != NULL); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = text->priv; - - g_object_ref (text); + priv = self->priv; if (color) { @@ -1454,22 +1561,26 @@ clutter_text_set_cursor_color (ClutterText *text, priv->cursor_color_set = TRUE; } else - { - priv->cursor_color_set = FALSE; - } + priv->cursor_color_set = FALSE; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-color"); + g_object_notify (G_OBJECT (self), "cursor-color-set"); } void -clutter_text_get_cursor_color (ClutterText *text, - ClutterColor *color) +clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); - priv = text->priv; + priv = self->priv; *color = priv->cursor_color; } @@ -1478,7 +1589,6 @@ gchar * clutter_text_get_selection (ClutterText *text) { ClutterTextPrivate *priv; - const gchar *utf8 = clutter_text_get_text (text); gchar *str; gint len; @@ -1635,12 +1745,10 @@ clutter_text_action_move_up (ClutterText *ttext, text = clutter_text_get_text (ttext); - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, - &x); + pango_layout_index_to_line_x (clutter_text_get_layout (ttext), + offset_to_bytes (text, priv->position), + 0, + &line_no, &x); if (priv->x_pos != -1) x = priv->x_pos; @@ -1709,10 +1817,12 @@ clutter_text_action_move_down (ClutterText *ttext, pango_layout_line_x_to_index (layout_line, x, &index_, NULL); - { - gint pos = bytes_to_offset (text, index_); - clutter_text_set_cursor_position (ttext, pos); - } + { + gint pos = bytes_to_offset (text, index_); + + clutter_text_set_cursor_position (ttext, pos); + } + if (!(priv->selectable && event && (event->key.modifier_state & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (ttext); @@ -1992,21 +2102,54 @@ clutter_text_get_text (ClutterText *text) } void -clutter_text_set_text (ClutterText *text, - const gchar *str) +clutter_text_set_text (ClutterText *self, + const gchar *text) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = text->priv; + priv = self->priv; - g_free (priv->text); - priv->text = g_strdup (str); + if (priv->max_length > 0) + { + gint len = g_utf8_strlen (text, -1); - clutter_text_dirty_cache (text); + if (len < priv->max_length) + { + g_free (priv->text); - clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + priv->text = g_strdup (text); + priv->n_bytes = priv->text ? strlen (priv->text) : 0; + priv->n_chars = len; + } + else + { + gchar *n = g_malloc0 (priv->max_length + 1); + + g_free (priv->text); + + g_utf8_strncpy (n, text, priv->max_length); + + priv->text = n; + priv->n_bytes = strlen (n); + priv->n_chars = priv->max_length; + } + } + else + { + g_free (priv->text); + + priv->text = g_strdup (text); + priv->n_bytes = priv->text ? strlen (priv->text) : 0; + priv->n_chars = priv->text ? g_utf8_strlen (priv->text, -1) : 0; + } + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_signal_emit (self, text_signals[TEXT_CHANGED], 0); g_object_notify (G_OBJECT (text), "text"); } @@ -2464,6 +2607,177 @@ clutter_text_set_cursor_position (ClutterText *self, clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } +/** + * clutter_text_set_text_visible: + * @self: a #ClutterText + * @visible: %TRUE if the contents of the actor are displayed as plain text. + * + * Sets whether the contents of the text actor are visible or not. When + * visibility is set to %FALSE, characters are displayed as the invisible + * char, and will also appear that way when the text in the text actor is + * copied elsewhere. + * + * The default invisible char is the asterisk '*', but it can be changed with + * clutter_text_set_invisible_char(). + * + * Since: 1.0 + */ +void +clutter_text_set_text_visible (ClutterText *self, + gboolean visible) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->text_visible != visible) + { + priv->text_visible = visible; + + clutter_text_dirty_cache (self); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "text-visible"); + } +} + +/** + * clutter_text_get_text_visible: + * @self: a #ClutterText + * + * Retrieves the actor's text visibility. + * + * Return value: %TRUE if the contents of the actor are displayed as plaintext + * + * Since: 1.0 + */ +gboolean +clutter_text_get_text_visible (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); + + return self->priv->text_visible; +} + +/** + * clutter_text_set_invisible_char: + * @self: a #ClutterText + * @wc: a Unicode character + * + * Sets the character to use in place of the actual text when + * clutter_text_set_text_visible() has been called to set text visibility + * to %FALSE. i.e. this is the character used in "password mode" to show the + * user how many characters have been typed. The default invisible char is an + * asterisk ('*'). If you set the invisible char to 0, then the user will get + * no feedback at all: there will be no text on the screen as they type. + * + * Since: 1.0 + */ +void +clutter_text_set_invisible_char (ClutterText *self, + gunichar wc) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + priv->priv_char = wc; + + if (priv->text_visible) + { + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + } + + g_object_notify (G_OBJECT (self), "invisible-char"); +} + +/** + * clutter_text_get_invisible_char: + * @self: a #ClutterText + * + * Returns the character to use in place of the actual text when + * the #ClutterText:text-visibility property is set to %FALSE. + * + * Return value: a Unicode character + * + * Since: 1.0 + */ +gunichar +clutter_text_get_invisible_char (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), '*'); + + return self->priv->priv_char; +} + +#if 0 +/** + * clutter_text_set_max_length: + * @self: a #ClutterText + * @max: the maximum number of characters allowed in the text actor; 0 + * to disable or -1 to set the length of the current string + * + * Sets the maximum allowed length of the contents of the actor. If the + * current contents are longer than the given length, then they will be + * truncated to fit. + * + * Since: 0.4 + */ +void +clutter_text_set_max_length (ClutterText *self, + gint max) +{ + ClutterTextPrivate *priv; + gchar *new = NULL; + + g_return_if_fail (CLUTTER_IS_TEXT (entry)); + + priv = self->priv; + + if (priv->max_length != max) + { + g_object_ref (entry); + + if (max < 0) + max = g_utf8_strlen (priv->text, -1); + + priv->max_length = max; + + new = g_strdup (priv->text); + clutter_text_set_text (entry, new); + g_free (new); + + g_object_notify (G_OBJECT (entry), "max-length"); + g_object_unref (entry); + } +} + +/** + * clutter_text_get_max_length: + * @entry: a #ClutterText + * + * Gets the maximum length of text that can be set into @entry. + * See clutter_text_set_max_length(). + * + * Return value: the maximum number of characters. + * + * Since: 0.4 + */ +gint +clutter_text_get_max_length (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (entry), -1); + + return self->priv->max_length; +} +#endif + void clutter_text_insert_unichar (ClutterText *self, gunichar wc) @@ -2499,8 +2813,6 @@ clutter_text_insert_unichar (ClutterText *self, g_string_free (new, TRUE); g_object_unref (self); - - g_signal_emit (G_OBJECT (self), text_signals[TEXT_CHANGED], 0); } void @@ -2531,13 +2843,11 @@ clutter_text_delete_text (ClutterText *ttext, } new = g_string_new (text); - new = g_string_erase (new, start_bytes, end_bytes - start_bytes); clutter_text_set_text (ttext, new->str); g_string_free (new, TRUE); - g_signal_emit (G_OBJECT (ttext), text_signals[TEXT_CHANGED], 0); } diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 65c919796..9bc2741e8 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -3,9 +3,10 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2006-2008 OpenedHand + * Copyright (C) 2008 Intel Corporation. * - * Authored By Øyvind Kolås + * Authored By: Øyvind Kolås + * Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,8 +29,8 @@ #ifndef __CLUTTER_TEXT_H__ #define __CLUTTER_TEXT_H__ -#include -#include +#include +#include G_BEGIN_DECLS @@ -54,13 +55,17 @@ struct _ClutterText struct _ClutterTextClass { + /*< private >*/ ClutterActorClass parent_class; + /*< public >*/ void (* text_changed) (ClutterText *self); void (* activate) (ClutterText *self); void (* cursor_event) (ClutterText *self, ClutterGeometry *geometry); + /*< private >*/ + /* padding for future expansion */ void (* _clutter_reserved1) (void); void (* _clutter_reserved2) (void); void (* _clutter_reserved3) (void); @@ -151,14 +156,14 @@ void clutter_text_set_selection_bound (ClutterText *self gint selection_bound); gint clutter_text_get_selection_bound (ClutterText *self); gchar * clutter_text_get_selection (ClutterText *self); -void clutter_text_set_visibility (ClutterText *self, +void clutter_text_set_text_visible (ClutterText *self, gboolean visible); -gboolean clutter_text_get_visibility (ClutterText *self); +gboolean clutter_text_get_text_visible (ClutterText *self); void clutter_text_set_invisible_char (ClutterText *self, - gunichar wc); + gunichar wc); gunichar clutter_text_get_invisible_char (ClutterText *self); void clutter_text_set_max_length (ClutterText *self, - gint max); + gint max); gint clutter_text_get_max_length (ClutterText *self); /* add a custom action that can be used in keybindings */ From 7af992974e08707d583cdb94d7353e4e64ee5633 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:42:33 +0000 Subject: [PATCH 014/108] Initialize the cursor position By default, the cursor position is initialized to -1, meaning "place the cursor always at the end of the text". --- clutter/clutter-text.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index ffee909d9..2fe488bc3 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1391,6 +1391,8 @@ clutter_text_init (ClutterText *self) priv->font_name = g_strdup (DEFAULT_FONT_NAME); priv->font_desc = pango_font_description_from_string (priv->font_name); + priv->position = -1; + priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; From 74257dfa273a6f283e01bdc91d5a74dea0bf38e0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:43:37 +0000 Subject: [PATCH 015/108] Add missing basic constructor ClutterText should have an empty constructor, mostly for bindings to use, that just proxies call to g_object_new() without setting any property. --- clutter/clutter-text.c | 6 ++++++ clutter/clutter-text.h | 1 + 2 files changed, 7 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 2fe488bc3..83ab13513 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1407,6 +1407,12 @@ clutter_text_init (ClutterText *self) init_mappings (self); /* FIXME: free */ } +ClutterActor * +clutter_text_new (void) +{ + return g_object_new (CLUTTER_TYPE_TEXT, NULL); +} + ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 9bc2741e8..491e3a3ff 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -78,6 +78,7 @@ struct _ClutterTextClass GType clutter_text_get_type (void) G_GNUC_CONST; +ClutterActor * clutter_text_new (void); ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, const ClutterColor *color); From 9169dff794889a9533f1cf778d452fdfdcc4edae Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:43:56 +0000 Subject: [PATCH 016/108] Add text editing methods Port the text editing methods from ClutterEntry, so that ClutterText exposes the same API. --- clutter/clutter-text.c | 100 +++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 83ab13513..1fdb61ce4 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2824,7 +2824,28 @@ clutter_text_insert_unichar (ClutterText *self, } void -clutter_text_delete_text (ClutterText *ttext, +clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (text != NULL); + + priv = self->priv; + + new = g_string_new (priv->text); + new = g_string_insert (new, position, text); + + clutter_text_set_text (self, new->str); + + g_string_free (new, TRUE); +} + +void +clutter_text_delete_text (ClutterText *self, gssize start_pos, gssize end_pos) { @@ -2832,38 +2853,89 @@ clutter_text_delete_text (ClutterText *ttext, GString *new = NULL; gint start_bytes; gint end_bytes; - const gchar *text; - g_return_if_fail (CLUTTER_IS_TEXT (ttext)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); - priv = ttext->priv; - text = clutter_text_get_text (ttext); + priv = self->priv; if (end_pos == -1) { - start_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1) - 1); - end_bytes = offset_to_bytes (text, g_utf8_strlen (text, -1)); + start_bytes = offset_to_bytes (priv->text, + g_utf8_strlen (priv->text, -1) - 1); + end_bytes = offset_to_bytes (priv->text, + g_utf8_strlen (priv->text, -1)); } else { - start_bytes = offset_to_bytes (text, start_pos); - end_bytes = offset_to_bytes (text, end_pos); + start_bytes = offset_to_bytes (priv->text, start_pos); + end_bytes = offset_to_bytes (priv->text, end_pos); } - new = g_string_new (text); + new = g_string_new (priv->text); new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - clutter_text_set_text (ttext, new->str); + clutter_text_set_text (self, new->str); g_string_free (new, TRUE); } +void +clutter_text_delete_chars (ClutterText *self, + guint n_chars) +{ + ClutterTextPrivate *priv; + GString *new = NULL; + gint len; + gint pos; + gint num_pos; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (!priv->text) + return; + + len = g_utf8_strlen (priv->text, -1); + new = g_string_new (priv->text); + + if (priv->position == -1) + { + num_pos = offset_to_bytes (priv->text, len - n_chars); + new = g_string_erase (new, num_pos, -1); + } + else + { + pos = offset_to_bytes (priv->text, priv->position - n_chars); + num_pos = offset_to_bytes (priv->text, priv->position); + new = g_string_erase (new, pos, num_pos - pos); + } + + clutter_text_set_text (self, new->str); + + if (priv->position > 0) + clutter_text_set_cursor_position (self, priv->position - n_chars); + + g_string_free (new, TRUE); + + g_object_notify (G_OBJECT (self), "text"); +} + +gchar * +clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + return NULL; +} void clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline) + guint keyval, + ClutterModifierType state, + const gchar *commandline) { ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); ClutterTextPrivate *priv = ttext->priv; From 94789e3cc3c84acbb63cc3e83a8fb949dd5186f5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:45:37 +0000 Subject: [PATCH 017/108] Add a separate test unit for ClutterText Instead of changing the unit for ClutterEntry, we add a new test unit specifically for ClutterText so that we can later tweak it specifically for the behaviour changes needed to make ClutterText work better. --- tests/conform/Makefile.am | 3 +- tests/conform/test-clutter-entry.c | 2 +- tests/conform/test-clutter-text.c | 370 +++++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 17 ++ 4 files changed, 390 insertions(+), 2 deletions(-) create mode 100644 tests/conform/test-clutter-text.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 985d74125..774156cfc 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -22,7 +22,8 @@ test_conformance_SOURCES = \ test-actor-invariants.c \ test-paint-opacity.c \ test-backface-culling.c \ - test-binding-pool.c + test-binding-pool.c \ + test-clutter-text.c # For convenience, this provides a way to easily run individual unit tests: .PHONY: wrappers diff --git a/tests/conform/test-clutter-entry.c b/tests/conform/test-clutter-entry.c index 6562836b1..a12d46b75 100644 --- a/tests/conform/test-clutter-entry.c +++ b/tests/conform/test-clutter-entry.c @@ -10,7 +10,7 @@ typedef struct { gint nbytes; } TestData; -const TestData +static const TestData test_data[] = { { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c new file mode 100644 index 000000000..5080423d0 --- /dev/null +++ b/tests/conform/test-clutter-text.c @@ -0,0 +1,370 @@ +#include +#include +#include + +#include "test-conform-common.h" + +typedef struct { + gunichar unichar; + const char bytes[6]; + gint nbytes; +} TestData; + +static const TestData +test_data[] = { + { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ + { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ +}; + +void +test_text_utf8_validation (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + gunichar unichar; + char bytes[6]; + int nbytes; + + g_assert (g_unichar_validate (t->unichar)); + + nbytes = g_unichar_to_utf8 (t->unichar, bytes); + bytes[nbytes] = '\0'; + g_assert (nbytes == t->nbytes); + g_assert (memcmp (t->bytes, bytes, nbytes) == 0); + + unichar = g_utf8_get_char_validated (bytes, nbytes); + g_assert (unichar == t->unichar); + } +} + +static int +get_nbytes (ClutterText *text) +{ + const char *s = clutter_text_get_text (text); + return strlen (s); +} + +static int +get_nchars (ClutterText *text) +{ + const char *s = clutter_text_get_text (text); + g_assert (g_utf8_validate (s, -1, NULL)); + return g_utf8_strlen (s, -1); +} + +#define DONT_MOVE_CURSOR (-2) + +static void +insert_unichar (ClutterText *text, gunichar unichar, int position) +{ + if (position > DONT_MOVE_CURSOR) + { + clutter_text_set_cursor_position (text, position); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, position); + } + + clutter_text_insert_unichar (text, unichar); +} + +void +test_text_empty (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + g_assert (clutter_text_get_text (text) == NULL); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_set_empty (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + /* annoyingly slightly different from initially empty */ + clutter_text_set_text (text, ""); + g_assert_cmpint (get_nchars (text), ==, 0); + g_assert_cmpint (get_nbytes (text), ==, 0); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_set_text (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + clutter_text_set_text (text, "abcdef"); + g_assert_cmpint (get_nchars (text), ==, 6); + g_assert_cmpint (get_nbytes (text), ==, 6); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_text_set_cursor_position (text, 5); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 5); + + clutter_text_set_text (text, ""); + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_append_some (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 1; j <= 4; j++) + { + insert_unichar (text, t->unichar, DONT_MOVE_CURSOR); + g_assert_cmpint (get_nchars (text), ==, j); + g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + } + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_prepend_some (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + clutter_text_insert_unichar (text, t->unichar); + g_assert_cmpint (get_nchars (text), ==, 1); + g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + for (j = 2; j <= 4; j++) + { + insert_unichar (text, t->unichar, 0); + g_assert_cmpint (get_nchars (text), ==, j); + g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + } + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_insert (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + + clutter_text_insert_unichar (text, t->unichar); + clutter_text_insert_unichar (text, t->unichar); + + insert_unichar (text, t->unichar, 1); + g_assert_cmpint (get_nchars (text), ==, 3); + g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 2); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_delete_chars (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 0; j < 4; j++) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 2); + clutter_text_delete_chars (text, 1); + g_assert_cmpint (get_nchars (text), ==, 3); + g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + clutter_text_set_cursor_position (text, 2); + clutter_text_delete_chars (text, 1); + g_assert_cmpint (get_nchars (text), ==, 2); + g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_delete_text (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 0; j < 4; j++) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 3); + clutter_text_delete_text (text, 2, 4); + + g_assert_cmpint (get_nchars (text), ==, 2); + g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); + + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +static void +init_event (ClutterKeyEvent *event) +{ + event->type = CLUTTER_KEY_PRESS; + event->time = 0; /* not needed */ + event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC; + event->stage = NULL; /* not needed */ + event->source = NULL; /* not needed */ + event->modifier_state = 0; + event->hardware_keycode = 0; /* not needed */ +} + +static void +send_keyval (ClutterText *text, int keyval) +{ + ClutterKeyEvent event; + + init_event (&event); + event.keyval = keyval; + event.unicode_value = 0; /* should be ignored for cursor keys etc. */ + + clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); +} + +static inline void +send_unichar (ClutterText *text, gunichar unichar) +{ + ClutterKeyEvent event; + + init_event (&event); + event.keyval = 0; /* should be ignored for printable characters */ + event.unicode_value = unichar; + + clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); +} + +void +test_text_cursor (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + int j; + + for (j = 0; j < 4; ++j) + clutter_text_insert_unichar (text, t->unichar); + + clutter_text_set_cursor_position (text, 2); + + /* test cursor moves and is clamped */ + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); + + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); + + send_keyval (text, CLUTTER_Left); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); + + /* delete text containing the cursor */ + clutter_text_set_cursor_position (text, 3); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 3); + + clutter_text_delete_text (text, 2, 4); + send_keyval (text, CLUTTER_Left); + + /* FIXME: cursor position should be -1? + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + */ + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + +void +test_text_event (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_data); i++) + { + const TestData *t = &test_data[i]; + + send_unichar (text, t->unichar); + + g_assert_cmpint (get_nchars (text), ==, 1); + g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); + g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); + + clutter_text_set_text (text, ""); + } + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index ff9535baf..9fb08c58d 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -70,6 +70,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/label", test_label_cache); + /* ClutterEntry */ TEST_CONFORM_SIMPLE ("/entry", test_entry_utf8_validation); TEST_CONFORM_SIMPLE ("/entry", test_entry_empty); TEST_CONFORM_SIMPLE ("/entry", test_entry_set_empty); @@ -85,6 +86,22 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/entry", test_entry_cursor); TEST_CONFORM_SIMPLE ("/entry", test_entry_event); + /* ClutterText */ + TEST_CONFORM_SIMPLE ("/text", test_text_utf8_validation); + TEST_CONFORM_SIMPLE ("/text", test_text_empty); + TEST_CONFORM_SIMPLE ("/text", test_text_set_empty); + TEST_CONFORM_SIMPLE ("/text", test_text_set_text); + + TEST_CONFORM_SIMPLE ("/text", test_text_append_some); + TEST_CONFORM_SIMPLE ("/text", test_text_prepend_some); + TEST_CONFORM_SIMPLE ("/text", test_text_insert); + + TEST_CONFORM_SIMPLE ("/text", test_text_delete_chars); + TEST_CONFORM_SIMPLE ("/text", test_text_delete_text); + + TEST_CONFORM_SIMPLE ("/text", test_text_cursor); + TEST_CONFORM_SIMPLE ("/text", test_text_event); + TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); From 7fa93ebe9ed7be18a6dba6174020434759db691b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:47:06 +0000 Subject: [PATCH 018/108] Replace offset_to_bytes() implementation We should re-use the offset_to_bytes() implementation from ClutterEntry as it guaranteed some behaviour and sanity checks that we want to keep inside ClutterText. --- clutter/clutter-text.c | 46 +++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 1fdb61ce4..1c6103568 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -182,15 +182,47 @@ enum static guint text_signals[LAST_SIGNAL] = { 0, }; -#define offset_real(text, pos) \ - (pos == -1 ? g_utf8_strlen (text, -1) : pos) +#define offset_real(t,p) \ + ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) -#define offset_to_bytes(text,pos) \ - (pos == -1 ? strlen (text) \ - : ((gint) (g_utf8_offset_to_pointer (text, pos) - text))) +static gint +offset_to_bytes (const gchar *text, + gint pos) +{ + gchar *c = NULL; + gint i, j, len; -#define bytes_to_offset(text, pos) \ - (g_utf8_pointer_to_offset (text, text + pos)) + if (pos < 0) + return strlen (text); + +#if 0 + if (pos < 1) + return pos; +#endif + + c = g_utf8_next_char (text); + j = 1; + len = strlen (text); + + for (i = 0; i < len; i++) + { + if (&text[i] == c) + { + if (j == pos) + break; + else + { + c = g_utf8_next_char (c); + j++; + } + } + } + + return i; +} + +#define bytes_to_offset(t,p) \ + (g_utf8_pointer_to_offset ((t), (t) + (p))) typedef struct TextCommand { From bdb0cc462d547c0e553c02f1692f35e60034d338 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:48:17 +0000 Subject: [PATCH 019/108] General whitespace fixes in ClutterText Let's keep whitespace fixes to their own commit to avoid polluting git-blame. --- clutter/clutter-text.c | 105 ++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 1c6103568..43ab3a2d1 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -478,68 +478,80 @@ clutter_text_coords_to_position (ClutterText *text, } static gboolean -clutter_text_position_to_coords (ClutterText *ttext, - gint position, - gint *x, - gint *y, - gint *cursor_height) +clutter_text_position_to_coords (ClutterText *self, + gint position, + gint *x, + gint *y, + gint *cursor_height) { - ClutterTextPrivate *priv; - gint index_; - PangoRectangle rect; - const gchar *text; + ClutterTextPrivate *priv = self->priv; + PangoRectangle rect; + gint priv_char_bytes; + gint index_; - text = clutter_text_get_text (ttext); - - priv = ttext->priv; + if (!priv->text_visible && priv->priv_char) + priv_char_bytes = g_unichar_to_utf8 (priv->priv_char, NULL); + else + priv_char_bytes = 1; if (position == -1) - index_ = strlen (text); + { + if (priv->text_visible) + index_ = strlen (priv->text); + else + index_ = priv->n_chars * priv_char_bytes; + } else - index_ = offset_to_bytes (text, position); + { + if (priv->text_visible) + index_ = offset_to_bytes (priv->text, position); + else + index_ = priv->position * priv_char_bytes; + } - if (index_ > strlen (text)) - index_ = strlen (text); - - pango_layout_get_cursor_pos (clutter_text_get_layout (ttext), - index_, + pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_, &rect, NULL); if (x) *x = rect.x / PANGO_SCALE; + if (y) *y = (rect.y + rect.height) / PANGO_SCALE; + if (cursor_height) *cursor_height = rect.height / PANGO_SCALE; return TRUE; /* FIXME: should return false if coords were outside text */ } -static void -clutter_text_ensure_cursor_position (ClutterText *ttext) +static inline void +clutter_text_ensure_cursor_position (ClutterText *self) { - gint x,y,cursor_height; - - ClutterTextPrivate *priv; - priv = ttext->priv; + ClutterTextPrivate *priv = self->priv; + gint x, y, cursor_height; - clutter_text_position_to_coords (ttext, priv->position, &x, &y, &cursor_height); + clutter_text_position_to_coords (self, priv->position, + &x, &y, + &cursor_height); priv->cursor_pos.x = x; priv->cursor_pos.y = y - cursor_height; - priv->cursor_pos.width = 2; + priv->cursor_pos.width = 2; priv->cursor_pos.height = cursor_height; - g_signal_emit (ttext, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); + g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } -static inline gboolean +static gboolean clutter_text_truncate_selection_internal (ClutterText *self) { ClutterTextPrivate *priv = self->priv; gint start_index; gint end_index; + if (!priv->text) + return TRUE; + start_index = offset_real (priv->text, priv->position); end_index = offset_real (priv->text, priv->selection_bound); @@ -1424,6 +1436,7 @@ clutter_text_init (ClutterText *self) priv->font_desc = pango_font_description_from_string (priv->font_name); priv->position = -1; + priv->selection_bound = -1; priv->x_pos = -1; priv->cursor_visible = TRUE; @@ -1435,6 +1448,8 @@ clutter_text_init (ClutterText *self) priv->text_visible = TRUE; priv->priv_char = '*'; + priv->max_length = 0; + init_commands (self); /* FIXME: free */ init_mappings (self); /* FIXME: free */ } @@ -1696,9 +1711,9 @@ clutter_text_get_selection_bound (ClutterText *self) /****************************************************************/ static gboolean -clutter_text_action_activate (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_activate (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { g_signal_emit (G_OBJECT (ttext), text_signals[ACTIVATE], 0); return TRUE; @@ -1708,13 +1723,14 @@ static void clutter_text_clear_selection (ClutterText *ttext) { ClutterTextPrivate *priv = ttext->priv; + priv->selection_bound = priv->position; } static gboolean clutter_text_action_move_left (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint pos = priv->position; @@ -1772,9 +1788,9 @@ clutter_text_action_move_right (ClutterText *ttext, } static gboolean -clutter_text_action_move_up (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_up (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -1799,9 +1815,8 @@ clutter_text_action_move_up (ClutterText *ttext, if (line_no < 0) return FALSE; - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); if (!layout_line) return TRUE; @@ -1987,14 +2002,16 @@ clutter_text_action_delete_next (ClutterText *ttext, gint pos; gint len; - if (clutter_text_truncate_selection (ttext, NULL, 0)) + if (clutter_text_truncate_selection_internal (ttext)) return TRUE; + priv = ttext->priv; pos = priv->position; len = g_utf8_strlen (clutter_text_get_text (ttext), -1); if (len && pos != -1 && pos < len) - clutter_text_delete_text (ttext, pos, pos+1);; + clutter_text_delete_text (ttext, pos, pos+1); + return TRUE; } @@ -2007,8 +2024,9 @@ clutter_text_action_delete_previous (ClutterText *ttext, gint pos; gint len; - if (clutter_text_truncate_selection (ttext, NULL, 0)) + if (clutter_text_truncate_selection_internal (ttext)) return TRUE; + priv = ttext->priv; pos = priv->position; len = g_utf8_strlen (clutter_text_get_text (ttext), -1); @@ -2148,6 +2166,7 @@ clutter_text_set_text (ClutterText *self, ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); + g_return_if_fail (text != NULL); priv = self->priv; From c72722df8ce3d3c91a26b8fc41cb7af9c6b6d864 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:49:14 +0000 Subject: [PATCH 020/108] Add :cursor-color-set property declaration The :cursor-color-set property is a read-only property that reflects whether the ClutterText actor is going to use the color set inside the :cursor-color property when painting the cursor. --- clutter/clutter-text.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 43ab3a2d1..8395266eb 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -702,6 +702,10 @@ clutter_text_get_property (GObject *gobject, clutter_value_set_color (value, &priv->cursor_color); break; + case PROP_CURSOR_COLOR_SET: + g_value_set_boolean (value, priv->cursor_color_set); + break; + case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); break; @@ -1215,12 +1219,19 @@ clutter_text_class_init (ClutterTextClass *klass) g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); pspec = clutter_param_spec_color ("cursor-color", - "Cursor Colour", - "Cursor Colour", + "Cursor Color", + "Cursor Color", &default_cursor_color, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); + pspec = g_param_spec_boolean ("cursor-color-set", + "Cursor Color Set", + "Whether the cursor color has been set", + FALSE, + CLUTTER_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec); + /** * ClutterText:position: * From 4cc57bdc4178571c7c296b09d09b8da93c828ead Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:49:45 +0000 Subject: [PATCH 021/108] Improve the safety checks inside the text setters We should check that the contents of the Text actor are not NULL when computing the offsets in bytes. --- clutter/clutter-text.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 8395266eb..b12ca9697 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2190,7 +2190,7 @@ clutter_text_set_text (ClutterText *self, g_free (priv->text); priv->text = g_strdup (text); - priv->n_bytes = priv->text ? strlen (priv->text) : 0; + priv->n_bytes = strlen (text); priv->n_chars = len; } else @@ -2211,8 +2211,8 @@ clutter_text_set_text (ClutterText *self, g_free (priv->text); priv->text = g_strdup (text); - priv->n_bytes = priv->text ? strlen (priv->text) : 0; - priv->n_chars = priv->text ? g_utf8_strlen (priv->text, -1) : 0; + priv->n_bytes = strlen (text); + priv->n_chars = g_utf8_strlen (text, -1); } clutter_text_dirty_cache (self); @@ -2221,7 +2221,7 @@ clutter_text_set_text (ClutterText *self, g_signal_emit (self, text_signals[TEXT_CHANGED], 0); - g_object_notify (G_OBJECT (text), "text"); + g_object_notify (G_OBJECT (self), "text"); } PangoLayout * @@ -2663,9 +2663,6 @@ clutter_text_set_cursor_position (ClutterText *self, priv = self->priv; - if (priv->text == NULL) - return; - len = g_utf8_strlen (priv->text, -1); if (position < 0 || position >= len) @@ -2862,14 +2859,15 @@ clutter_text_insert_unichar (ClutterText *self, if (wc == 0) return; - clutter_text_truncate_selection (self, NULL, 0); - priv = self->priv; - g_object_ref (self); - new = g_string_new (priv->text); - pos = offset_to_bytes (priv->text, priv->position); + + if (priv->text) + pos = offset_to_bytes (priv->text, priv->position); + else + pos = 0; + new = g_string_insert_unichar (new, pos, wc); clutter_text_set_text (self, new->str); @@ -2881,8 +2879,6 @@ clutter_text_insert_unichar (ClutterText *self, } g_string_free (new, TRUE); - - g_object_unref (self); } void @@ -2920,6 +2916,9 @@ clutter_text_delete_text (ClutterText *self, priv = self->priv; + if (!priv->text) + return; + if (end_pos == -1) { start_bytes = offset_to_bytes (priv->text, From 1ff02f570e0dd214ffc4a29d82b8876b362d74f9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:50:02 +0000 Subject: [PATCH 022/108] Use the stored contents length in ::delete_chars() Instead of recomputing it, we can reuse the contents length we compute and store inside clutter_text_set_text(). --- clutter/clutter-text.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index b12ca9697..d5213a46f 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2962,7 +2962,7 @@ clutter_text_delete_chars (ClutterText *self, if (priv->position == -1) { - num_pos = offset_to_bytes (priv->text, len - n_chars); + num_pos = offset_to_bytes (priv->text, priv->n_chars - n_chars); new = g_string_erase (new, num_pos, -1); } else From 191d1aebff0dce424f67a4ac9065570d35f519b7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:51:28 +0000 Subject: [PATCH 023/108] Update the ClutterText test unit Whenever we are sending specially crafted KeyEvents to a ClutterText we also need to set it editable, since the event handling code depends on the editability setting. --- tests/conform/test-clutter-text.c | 44 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index 5080423d0..993478c4d 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -11,7 +11,7 @@ typedef struct { } TestData; static const TestData -test_data[] = { +test_text_data[] = { { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ }; @@ -22,9 +22,9 @@ test_text_utf8_validation (TestConformSimpleFixture *fixture, { int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; gunichar unichar; char bytes[6]; int nbytes; @@ -111,8 +111,8 @@ test_text_set_text (TestConformSimpleFixture *fixture, clutter_text_set_cursor_position (text, 5); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 5); - clutter_text_set_text (text, ""); /* FIXME: cursor position should be -1? + clutter_text_set_text (text, ""); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); */ @@ -126,14 +126,15 @@ test_text_append_some (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 1; j <= 4; j++) { insert_unichar (text, t->unichar, DONT_MOVE_CURSOR); + g_assert_cmpint (get_nchars (text), ==, j); g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); @@ -152,9 +153,9 @@ test_text_prepend_some (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; clutter_text_insert_unichar (text, t->unichar); @@ -183,14 +184,15 @@ test_text_insert (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; clutter_text_insert_unichar (text, t->unichar); clutter_text_insert_unichar (text, t->unichar); insert_unichar (text, t->unichar, 1); + g_assert_cmpint (get_nchars (text), ==, 3); g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 2); @@ -208,9 +210,9 @@ test_text_delete_chars (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; j++) @@ -241,9 +243,9 @@ test_text_delete_text (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; j++) @@ -289,7 +291,7 @@ send_keyval (ClutterText *text, int keyval) clutter_actor_event (CLUTTER_ACTOR (text), (ClutterEvent *) &event, FALSE); } -static inline void +static void send_unichar (ClutterText *text, gunichar unichar) { ClutterKeyEvent event; @@ -308,9 +310,11 @@ test_text_cursor (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; ++j) @@ -352,9 +356,11 @@ test_text_event (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; - for (i = 0; i < G_N_ELEMENTS (test_data); i++) + clutter_text_set_editable (text, TRUE); + + for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { - const TestData *t = &test_data[i]; + const TestData *t = &test_text_data[i]; send_unichar (text, t->unichar); From d10f7127a2c07cf80c10c35eeacaa4b20269f740 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:54:07 +0000 Subject: [PATCH 024/108] Implement Text:max-length The :max-length property establishes the maximum available length for the Text actor's contents, in characters. --- clutter/clutter-text.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index d5213a46f..20489da75 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -168,7 +168,8 @@ enum PROP_SELECTABLE, PROP_ACTIVATABLE, PROP_TEXT_VISIBLE, - PROP_INVISIBLE_CHAR + PROP_INVISIBLE_CHAR, + PROP_MAX_LENGTH }; enum @@ -667,6 +668,10 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_invisible_char (self, g_value_get_uint (value)); break; + case PROP_MAX_LENGTH: + clutter_text_set_max_length (self, g_value_get_int (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -734,6 +739,10 @@ clutter_text_get_property (GObject *gobject, g_value_set_uint (value, priv->priv_char); break; + case PROP_MAX_LENGTH: + g_value_set_int (value, priv->max_length); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -1373,6 +1382,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + pspec = g_param_spec_int ("max-length", + "Max Length", + "Maximum length of the text inside the actor", + -1, G_MAXINT, 0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec); + /** * ClutterText::text-changed: * @actor: the actor which received the event @@ -2783,7 +2799,6 @@ clutter_text_get_invisible_char (ClutterText *self) return self->priv->priv_char; } -#if 0 /** * clutter_text_set_max_length: * @self: a #ClutterText @@ -2794,56 +2809,53 @@ clutter_text_get_invisible_char (ClutterText *self) * current contents are longer than the given length, then they will be * truncated to fit. * - * Since: 0.4 + * Since: 1.0 */ void clutter_text_set_max_length (ClutterText *self, - gint max) + gint max) { ClutterTextPrivate *priv; gchar *new = NULL; - g_return_if_fail (CLUTTER_IS_TEXT (entry)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->max_length != max) { - g_object_ref (entry); - if (max < 0) max = g_utf8_strlen (priv->text, -1); priv->max_length = max; new = g_strdup (priv->text); - clutter_text_set_text (entry, new); + clutter_text_set_text (self, new); g_free (new); - g_object_notify (G_OBJECT (entry), "max-length"); - g_object_unref (entry); + g_object_notify (G_OBJECT (self), "max-length"); } } /** * clutter_text_get_max_length: - * @entry: a #ClutterText + * @self: a #ClutterText + * + * Gets the maximum length of text that can be set into a text actor. * - * Gets the maximum length of text that can be set into @entry. * See clutter_text_set_max_length(). * * Return value: the maximum number of characters. * - * Since: 0.4 + * Since: 1.0 */ gint clutter_text_get_max_length (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (entry), -1); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); return self->priv->max_length; } -#endif void clutter_text_insert_unichar (ClutterText *self, From d84a88ac3b2a7dbc528c7f00a2dd0fc877d37783 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:54:32 +0000 Subject: [PATCH 025/108] Coding style consistency Channel mitch's spirit. This is also how I get to have the highest commit count on Clutter's repository. --- clutter/clutter-text.c | 126 ++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 20489da75..93809bf5c 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1755,7 +1755,7 @@ clutter_text_clear_selection (ClutterText *ttext) } static gboolean -clutter_text_action_move_left (ClutterText *ttext, +clutter_text_action_move_left (ClutterText *ttext, const gchar *commandline, ClutterEvent *event) { @@ -1785,9 +1785,9 @@ clutter_text_action_move_left (ClutterText *ttext, static gboolean -clutter_text_action_move_right (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_right (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint pos; @@ -1863,9 +1863,9 @@ clutter_text_action_move_up (ClutterText *ttext, } static gboolean -clutter_text_action_move_down (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_down (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -1888,14 +1888,12 @@ clutter_text_action_move_down (ClutterText *ttext, else priv->x_pos = x; - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no + 1); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), + line_no + 1); if (!layout_line) - { - return FALSE; - } + return FALSE; pango_layout_line_x_to_index (layout_line, x, &index_, NULL); @@ -1912,23 +1910,25 @@ clutter_text_action_move_down (ClutterText *ttext, } static gboolean -clutter_text_action_move_start (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_start (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; clutter_text_set_cursor_position (ttext, 0); + if (!(priv->selectable && event && (event->key.modifier_state & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (ttext); + return TRUE; } static gboolean -clutter_text_action_move_end (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_end (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; @@ -1941,9 +1941,9 @@ clutter_text_action_move_end (ClutterText *ttext, } static gboolean -clutter_text_action_move_start_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_start_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -1962,9 +1962,8 @@ clutter_text_action_move_start_line (ClutterText *ttext, &line_no, NULL); - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); @@ -1979,9 +1978,9 @@ clutter_text_action_move_start_line (ClutterText *ttext, } static gboolean -clutter_text_action_move_end_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_move_end_line (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv = ttext->priv; gint line_no; @@ -2002,9 +2001,8 @@ clutter_text_action_move_end_line (ClutterText *ttext, &line_no, NULL); - layout_line = pango_layout_get_line_readonly ( - clutter_text_get_layout (ttext), - line_no); + layout_line = + pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); index_ += trailing; @@ -2021,9 +2019,9 @@ clutter_text_action_move_end_line (ClutterText *ttext, } static gboolean -clutter_text_action_delete_next (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_delete_next (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv; gint pos; @@ -2043,9 +2041,9 @@ clutter_text_action_delete_next (ClutterText *ttext, } static gboolean -clutter_text_action_delete_previous (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) +clutter_text_action_delete_previous (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event) { ClutterTextPrivate *priv; gint pos; @@ -2075,11 +2073,13 @@ clutter_text_action_delete_previous (ClutterText *ttext, return TRUE; } -static void init_commands (ClutterText *ttext) +static void +init_commands (ClutterText *ttext) { ClutterTextPrivate *priv = ttext->priv; if (priv->commands) return; + clutter_text_add_action (ttext, "move-left", clutter_text_action_move_left); clutter_text_add_action (ttext, "move-right", clutter_text_action_move_right); clutter_text_add_action (ttext, "move-up", clutter_text_action_move_up); @@ -2095,9 +2095,9 @@ static void init_commands (ClutterText *ttext) } gboolean -clutter_text_action (ClutterText *ttext, - const gchar *command, - ClutterEvent *event) +clutter_text_action (ClutterText *ttext, + const gchar *command, + ClutterEvent *event) { gchar command2[64]; gint i; @@ -2137,18 +2137,18 @@ clutter_text_get_font_name (ClutterText *text) } void -clutter_text_set_font_name (ClutterText *text, +clutter_text_set_font_name (ClutterText *self, const gchar *font_name) { ClutterTextPrivate *priv; PangoFontDescription *desc; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); if (!font_name || font_name[0] == '\0') font_name = DEFAULT_FONT_NAME; - priv = text->priv; + priv = self->priv; if (priv->font_name && strcmp (priv->font_name, font_name) == 0) return; @@ -2170,20 +2170,20 @@ clutter_text_set_font_name (ClutterText *text, priv->font_desc = desc; - clutter_text_dirty_cache (text); + clutter_text_dirty_cache (self); if (priv->text && priv->text[0] != '\0') - clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); - g_object_notify (G_OBJECT (text), "font-name"); + g_object_notify (G_OBJECT (self), "font-name"); } G_CONST_RETURN gchar * -clutter_text_get_text (ClutterText *text) +clutter_text_get_text (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); - return text->priv->text; + return self->priv->text; } void @@ -2241,46 +2241,46 @@ clutter_text_set_text (ClutterText *self, } PangoLayout * -clutter_text_get_layout (ClutterText *text) +clutter_text_get_layout (ClutterText *self) { ClutterUnit width; - g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); - width = clutter_actor_get_widthu (CLUTTER_ACTOR (text)); + width = clutter_actor_get_widthu (CLUTTER_ACTOR (self)); - return clutter_text_create_layout (text, width); + return clutter_text_create_layout (self, width); } void -clutter_text_set_color (ClutterText *text, +clutter_text_set_color (ClutterText *self, const ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); - priv = text->priv; + priv = self->priv; priv->text_color = *color; - if (CLUTTER_ACTOR_IS_VISIBLE (text)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (text)); + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); - g_object_notify (G_OBJECT (text), "color"); + g_object_notify (G_OBJECT (self), "color"); } void -clutter_text_get_color (ClutterText *text, +clutter_text_get_color (ClutterText *self, ClutterColor *color) { ClutterTextPrivate *priv; - g_return_if_fail (CLUTTER_IS_TEXT (text)); + g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); - priv = text->priv; + priv = self->priv; *color = priv->text_color; } From 4a1f4d6f9aa3c89750e9eac5f4d95f799d2d2291 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:55:39 +0000 Subject: [PATCH 026/108] Move internal data structures on top Try to keep the declarations section of the source files clean and possibly consistent. --- clutter/clutter-text.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 93809bf5c..601f351a7 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -56,6 +56,8 @@ #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) typedef struct _LayoutCache LayoutCache; +typedef struct _TextCommand TextCommand; +typedef struct _ClutterTextMapping ClutterTextMapping; /* Probably move into main */ static PangoContext *_context = NULL; @@ -82,6 +84,21 @@ struct _LayoutCache guint age; }; +struct _TextCommand +{ + const gchar *name; + gboolean (*func) (ClutterText *ttext, + const gchar *commandline, + ClutterEvent *event); +}; + +struct _ClutterTextMapping +{ + ClutterModifierType state; + guint keyval; + const gchar *action; +}; + struct _ClutterTextPrivate { PangoFontDescription *font_desc; @@ -226,19 +243,6 @@ offset_to_bytes (const gchar *text, (g_utf8_pointer_to_offset ((t), (t) + (p))) -typedef struct TextCommand { - const gchar *name; - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); -} TextCommand; - -typedef struct ClutterTextMapping { - ClutterModifierType state; - guint keyval; - const gchar *action; -} ClutterTextMapping; - void clutter_text_mappings_clear (ClutterText *self) { From e93a7e243c3c43be59fbbfacaae9a55c5dd923a0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:56:44 +0000 Subject: [PATCH 027/108] Add comments in the test suite Comment why we need to enable the editability of the Text actor inside the test suite. This should clarify commit ea508ea528d61ae478d8bc4c88f54a89304f18e8 --- tests/conform/test-clutter-text.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index 993478c4d..e6662182a 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -159,6 +159,7 @@ test_text_prepend_some (TestConformSimpleFixture *fixture, int j; clutter_text_insert_unichar (text, t->unichar); + g_assert_cmpint (get_nchars (text), ==, 1); g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); @@ -166,6 +167,7 @@ test_text_prepend_some (TestConformSimpleFixture *fixture, for (j = 2; j <= 4; j++) { insert_unichar (text, t->unichar, 0); + g_assert_cmpint (get_nchars (text), ==, j); g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); @@ -310,6 +312,7 @@ test_text_cursor (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; + /* only editable entries listen to events */ clutter_text_set_editable (text, TRUE); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) @@ -356,6 +359,7 @@ test_text_event (TestConformSimpleFixture *fixture, ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; + /* only editable entries listen to events */ clutter_text_set_editable (text, TRUE); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) From 2fedd3263c4291aa2860e26ebface29913f909e7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:57:46 +0000 Subject: [PATCH 028/108] Do not namespace internal data structures Since the internal data structures are not exported (duh!), we can eschew the namespacing and save us some characters. --- clutter/clutter-text.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 601f351a7..db9cf469a 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -57,7 +57,7 @@ typedef struct _LayoutCache LayoutCache; typedef struct _TextCommand TextCommand; -typedef struct _ClutterTextMapping ClutterTextMapping; +typedef struct _TextMapping TextMapping; /* Probably move into main */ static PangoContext *_context = NULL; @@ -92,7 +92,7 @@ struct _TextCommand ClutterEvent *event); }; -struct _ClutterTextMapping +struct _TextMapping { ClutterModifierType state; guint keyval; @@ -976,7 +976,7 @@ clutter_text_key_press (ClutterActor *actor, for (iter = priv->mappings; iter != NULL; iter = iter->next) { - ClutterTextMapping *mapping = iter->data; + TextMapping *mapping = iter->data; if ( (mapping->keyval == keyval) && @@ -3014,7 +3014,7 @@ clutter_text_add_mapping (ClutterText *ttext, ClutterModifierType state, const gchar *commandline) { - ClutterTextMapping *tmapping = g_new (ClutterTextMapping, 1); + TextMapping *tmapping = g_new (TextMapping, 1); ClutterTextPrivate *priv = ttext->priv; tmapping->keyval = keyval; tmapping->state = state; From b1c366a143eb1db68b46cb235e43761dfbb93745 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 11:59:50 +0000 Subject: [PATCH 029/108] Add Text::get_chars() implementation and tests The clutter_text_get_chars() function returns a section of the contents of the Text actor, delimited by a start and an end position. This commit adds the implementation for that function and a test unit that guarantees the offset-to-bytes computations are correct. --- clutter/clutter-text.c | 35 ++++++++++++++++++++++++++++++- tests/conform/test-clutter-text.c | 31 +++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 7 +------ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index db9cf469a..678defbca 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2998,14 +2998,47 @@ clutter_text_delete_chars (ClutterText *self, g_object_notify (G_OBJECT (self), "text"); } +/** + * clutter_text_get_chars: + * @self: a #ClutterTex + * @start_pos: start of text, in characters + * @end_pos: end of text, in characters + * + * Retrieves the contents of the #ClutterText actor between + * @start_pos and @end_pos. + * + * The positions are specified in characters, not in bytes. + * + * Return value: a newly allocated string with the contents of + * the text actor between the specified positions. Use g_free() + * to free the resources when done + * + * Since: 1.0 + */ gchar * clutter_text_get_chars (ClutterText *self, gssize start_pos, gssize end_pos) { + ClutterTextPrivate *priv; + gint start_index, end_index; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); - return NULL; + priv = self->priv; + + if (end_pos < 0) + end_pos = priv->n_chars; + + start_pos = MIN (priv->n_chars, start_pos); + end_pos = MIN (priv->n_chars, end_pos); + + start_index = g_utf8_offset_to_pointer (priv->text, start_pos) + - priv->text; + end_index = g_utf8_offset_to_pointer (priv->text, end_pos) + - priv->text; + + return g_strndup (priv->text + start_index, end_index - start_index); } void diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index e6662182a..b47002a40 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -238,6 +238,37 @@ test_text_delete_chars (TestConformSimpleFixture *fixture, clutter_actor_destroy (CLUTTER_ACTOR (text)); } +void +test_text_get_chars (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + gchar *chars; + + clutter_text_set_text (text, "00abcdef11"); + g_assert_cmpint (get_nchars (text), ==, 10); + g_assert_cmpint (get_nbytes (text), ==, 10); + g_assert_cmpstr (clutter_text_get_text (text), ==, "00abcdef11"); + + chars = clutter_text_get_chars (text, 2, -1); + g_assert_cmpstr (chars, ==, "abcdef11"); + g_free (chars); + + chars = clutter_text_get_chars (text, 0, 8); + g_assert_cmpstr (chars, ==, "00abcdef"); + g_free (chars); + + chars = clutter_text_get_chars (text, 2, 8); + g_assert_cmpstr (chars, ==, "abcdef"); + g_free (chars); + + chars = clutter_text_get_chars (text, 8, 12); + g_assert_cmpstr (chars, ==, "11"); + g_free (chars); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + void test_text_delete_text (TestConformSimpleFixture *fixture, gconstpointer data) diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 9fb08c58d..d12c4b83a 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -75,14 +75,11 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/entry", test_entry_empty); TEST_CONFORM_SIMPLE ("/entry", test_entry_set_empty); TEST_CONFORM_SIMPLE ("/entry", test_entry_set_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_append_some); TEST_CONFORM_SIMPLE ("/entry", test_entry_prepend_some); TEST_CONFORM_SIMPLE ("/entry", test_entry_insert); - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_chars); TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_cursor); TEST_CONFORM_SIMPLE ("/entry", test_entry_event); @@ -91,16 +88,14 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/text", test_text_empty); TEST_CONFORM_SIMPLE ("/text", test_text_set_empty); TEST_CONFORM_SIMPLE ("/text", test_text_set_text); - TEST_CONFORM_SIMPLE ("/text", test_text_append_some); TEST_CONFORM_SIMPLE ("/text", test_text_prepend_some); TEST_CONFORM_SIMPLE ("/text", test_text_insert); - TEST_CONFORM_SIMPLE ("/text", test_text_delete_chars); TEST_CONFORM_SIMPLE ("/text", test_text_delete_text); - TEST_CONFORM_SIMPLE ("/text", test_text_cursor); TEST_CONFORM_SIMPLE ("/text", test_text_event); + TEST_CONFORM_SIMPLE ("/text", test_text_get_chars); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); From eae98800d2fd45cacbab9f82fb9345e7f23ea517 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:03:17 +0000 Subject: [PATCH 030/108] Use ClutterBindPool inside ClutterText ClutterText should use the newly added ClutterBindingPool API to handle key events, instead of its homegrown code. This commit removes the action/mapping code and defers the entire key binding matching to a ClutterBindingPool created inside the Text class initialization function. --- clutter/clutter-text.c | 971 +++++++++++++++++++---------------------- clutter/clutter-text.h | 20 - 2 files changed, 443 insertions(+), 548 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 678defbca..899c9a9cf 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -33,11 +33,12 @@ #include "clutter-text.h" +#include "clutter-binding-pool.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" #include "clutter-keysyms.h" #include "clutter-main.h" -#include "clutter-enum-types.h" #include "clutter-private.h" -#include "clutter-debug.h" #include "clutter-units.h" #include "cogl-pango.h" @@ -84,21 +85,6 @@ struct _LayoutCache guint age; }; -struct _TextCommand -{ - const gchar *name; - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); -}; - -struct _TextMapping -{ - ClutterModifierType state; - guint keyval; - const gchar *action; -}; - struct _ClutterTextPrivate { PangoFontDescription *font_desc; @@ -151,12 +137,6 @@ struct _ClutterTextPrivate ClutterGeometry cursor_pos; ClutterColor cursor_color; - GList *mappings; - GList *commands; /* each instance has it's own set of commands - so that actor specific actions can be added - to single actor classes - */ - gint max_length; gunichar priv_char; @@ -243,45 +223,12 @@ offset_to_bytes (const gchar *text, (g_utf8_pointer_to_offset ((t), (t) + (p))) -void -clutter_text_mappings_clear (ClutterText *self) +static inline void +clutter_text_clear_selection (ClutterText *self) { ClutterTextPrivate *priv = self->priv; - g_list_foreach (priv->mappings, (GFunc) g_free, NULL); - g_list_free (priv->mappings); - priv->mappings = NULL; -} - -static void init_commands (ClutterText *ttext); - -static void -init_mappings (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; - - if (priv->mappings) - return; - - clutter_text_add_mapping (ttext, CLUTTER_Left, 0, "move-left"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Left, 0, "move-left"); - clutter_text_add_mapping (ttext, CLUTTER_Right, 0, "move-right"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Right, 0,"move-right"); - clutter_text_add_mapping (ttext, CLUTTER_Up, 0, "move-up"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Up, 0, "move-up"); - clutter_text_add_mapping (ttext, CLUTTER_Down, 0, "move-down"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Down, 0, "move-down"); - clutter_text_add_mapping (ttext, CLUTTER_Begin, 0, "move-start-line"); - clutter_text_add_mapping (ttext, CLUTTER_Home, 0, "move-start-line"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Home, 0, "move-start-line"); - clutter_text_add_mapping (ttext, CLUTTER_End, 0, "move-end-line"); - clutter_text_add_mapping (ttext, CLUTTER_KP_End, 0, "move-end-line"); - clutter_text_add_mapping (ttext, CLUTTER_BackSpace, 0 , "delete-previous"); - clutter_text_add_mapping (ttext, CLUTTER_Delete, 0, "delete-next"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Delete,0, "delete-next"); - clutter_text_add_mapping (ttext, CLUTTER_Return, 0, "activate"); - clutter_text_add_mapping (ttext, CLUTTER_KP_Enter, 0, "activate"); - clutter_text_add_mapping (ttext, CLUTTER_ISO_Enter, 0, "activate"); + priv->selection_bound = priv->position; } static PangoLayout * @@ -548,7 +495,7 @@ clutter_text_ensure_cursor_position (ClutterText *self) } static gboolean -clutter_text_truncate_selection_internal (ClutterText *self) +clutter_text_truncate_selection (ClutterText *self) { ClutterTextPrivate *priv = self->priv; gint start_index; @@ -578,14 +525,6 @@ clutter_text_truncate_selection_internal (ClutterText *self) return TRUE; } -static gboolean -clutter_text_truncate_selection (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - return clutter_text_truncate_selection_internal (ttext); -} - static void clutter_text_set_property (GObject *gobject, guint prop_id, @@ -775,11 +714,6 @@ clutter_text_finalize (GObject *gobject) g_free (priv->text); g_free (priv->font_name); - clutter_text_mappings_clear (self); - - g_list_foreach (priv->commands, (GFunc) g_free, NULL); - g_list_free (priv->commands); - G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject); } @@ -877,8 +811,6 @@ cursor_paint (ClutterText *self) } } - - static gboolean clutter_text_button_press (ClutterActor *actor, ClutterButtonEvent *bev) @@ -910,7 +842,6 @@ clutter_text_button_press (ClutterActor *actor, return TRUE; } - static gboolean clutter_text_motion (ClutterActor *actor, ClutterMotionEvent *mev) @@ -965,37 +896,29 @@ clutter_text_button_release (ClutterActor *actor, static gboolean clutter_text_key_press (ClutterActor *actor, - ClutterKeyEvent *kev) + ClutterKeyEvent *event) { - ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; - gint keyval = clutter_key_event_symbol (kev); - GList *iter; + ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + ClutterBindingPool *pool; + gboolean res; + gint keyval; if (!priv->editable) return FALSE; - for (iter = priv->mappings; iter != NULL; iter = iter->next) - { - TextMapping *mapping = iter->data; + keyval = clutter_key_event_symbol (event); - if ( - (mapping->keyval == keyval) && - ( - (mapping->state == 0) || - (mapping->state && (kev->modifier_state & mapping->state)) - ) - ) - { - if (!g_str_equal (mapping->action, "activate") || - priv->activatable) - return clutter_text_action (CLUTTER_TEXT (actor), - mapping->action, - (ClutterEvent *) kev); - } - } + pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); + g_assert (pool != NULL); + res = clutter_binding_pool_activate (pool, keyval, + event->modifier_state, + G_OBJECT (actor)); + if (res) + return TRUE; + else { - gunichar key_unichar = clutter_key_event_unicode (kev); + gunichar key_unichar = clutter_key_event_unicode (event); if (key_unichar == '\r') /* return is reported as CR we want LF */ key_unichar = '\n'; @@ -1003,6 +926,7 @@ clutter_text_key_press (ClutterActor *actor, if (g_unichar_validate (key_unichar)) { clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + return TRUE; } } @@ -1126,11 +1050,340 @@ clutter_text_allocate (ClutterActor *self, parent_class->allocate (self, box, origin_changed); } +static gboolean +clutter_text_real_move_left (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos = priv->position; + gint len; + + len = g_utf8_strlen (priv->text, -1); + + if (pos != 0 && len !=0) + { + if (pos == -1) + clutter_text_set_cursor_position (self, len - 1); + else + clutter_text_set_cursor_position (self, pos - 1); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_right (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos = priv->position; + gint len; + + len = g_utf8_strlen (priv->text, -1); + + if (pos != -1 && len !=0) + { + if (pos != len) + clutter_text_set_cursor_position (self, pos + 1); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_up (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint x; + + layout = clutter_text_get_layout (self); + + pango_layout_index_to_line_x (layout, + offset_to_bytes (priv->text, priv->position), + 0, + &line_no, &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + line_no -= 1; + if (line_no < 0) + return FALSE; + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, pos); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_move_down (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint x; + + layout = clutter_text_get_layout (self); + + pango_layout_index_to_line_x (layout, + offset_to_bytes (priv->text, priv->position), + 0, + &line_no, &x); + + if (priv->x_pos != -1) + x = priv->x_pos; + else + priv->x_pos = x; + + layout_line = pango_layout_get_line_readonly (layout, line_no + 1); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, x, &index_, NULL); + + { + gint pos = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, pos); + } + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_line_start (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint position; + + layout = clutter_text_get_layout (self); + + pango_layout_index_to_line_x (layout, + offset_to_bytes (priv->text, priv->position), + 0, + &line_no, NULL); + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); + + position = bytes_to_offset (priv->text, index_); + clutter_text_set_cursor_position (self, position); + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_line_end (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + PangoLayoutLine *layout_line; + PangoLayout *layout; + gint line_no; + gint index_; + gint trailing; + gint position; + + layout = clutter_text_get_layout (self); + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, + 0, + &line_no, NULL); + + layout_line = pango_layout_get_line_readonly (layout, line_no); + if (!layout_line) + return FALSE; + + pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); + index_ += trailing; + + position = bytes_to_offset (priv->text, index_); + + clutter_text_set_cursor_position (self, position); + + if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) + clutter_text_clear_selection (self); + + return TRUE; +} + +static gboolean +clutter_text_real_del_next (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (self)) + return FALSE; + + pos = priv->position; + len = g_utf8_strlen (priv->text, -1); + + if (len && pos != -1 && pos < len) + { + clutter_text_delete_text (self, pos, pos + 1); + + return TRUE; + } + + return FALSE; +} + +static gboolean +clutter_text_real_del_prev (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + gint pos; + gint len; + + if (clutter_text_truncate_selection (self)) + return FALSE; + + pos = priv->position; + len = g_utf8_strlen (priv->text, -1); + + if (pos != 0 && len != 0) + { + if (pos == -1) + { + clutter_text_set_cursor_position (self, len - 1); + clutter_text_set_selection_bound (self, len - 1); + } + else + { + clutter_text_set_cursor_position (self, pos - 1); + clutter_text_set_selection_bound (self, pos - 1); + } + + clutter_text_delete_text (self, pos - 1, pos); + + return TRUE; + } + + return FALSE; +} + +static gboolean +clutter_text_real_activate (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterTextPrivate *priv = self->priv; + + if (priv->activatable) + { + g_signal_emit (self, text_signals[ACTIVATE], 0); + + return TRUE; + } + + return FALSE; +} + +static gboolean +clutter_text_real_page_up (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + return FALSE; +} + +static gboolean +clutter_text_real_page_down (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + return FALSE; +} + +static void +clutter_text_add_move_binding (ClutterBindingPool *pool, + const gchar *action, + guint key_val, + GCallback callback) +{ + clutter_binding_pool_install_action (pool, action, + key_val, 0, + callback, + NULL, NULL); + clutter_binding_pool_install_action (pool, action, + key_val, CLUTTER_SHIFT_MASK, + callback, + NULL, NULL); +} + static void clutter_text_class_init (ClutterTextClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + ClutterBindingPool *binding_pool; GParamSpec *pspec; _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); @@ -1437,6 +1690,97 @@ clutter_text_class_init (ClutterTextClass *klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + binding_pool = clutter_binding_pool_get_for_class (klass); + + clutter_text_add_move_binding (binding_pool, "move-left", + CLUTTER_Left, + G_CALLBACK (clutter_text_real_move_left)); + clutter_text_add_move_binding (binding_pool, "move-left", + CLUTTER_KP_Left, + G_CALLBACK (clutter_text_real_move_left)); + clutter_text_add_move_binding (binding_pool, "move-right", + CLUTTER_Right, + G_CALLBACK (clutter_text_real_move_right)); + clutter_text_add_move_binding (binding_pool, "move-right", + CLUTTER_KP_Right, + G_CALLBACK (clutter_text_real_move_right)); + clutter_text_add_move_binding (binding_pool, "move-up", + CLUTTER_Up, + G_CALLBACK (clutter_text_real_move_up)); + clutter_text_add_move_binding (binding_pool, "move-up", + CLUTTER_KP_Up, + G_CALLBACK (clutter_text_real_move_up)); + clutter_text_add_move_binding (binding_pool, "move-down", + CLUTTER_Down, + G_CALLBACK (clutter_text_real_move_down)); + clutter_text_add_move_binding (binding_pool, "move-down", + CLUTTER_KP_Down, + G_CALLBACK (clutter_text_real_move_down)); + + clutter_binding_pool_install_action (binding_pool, "line-start", + CLUTTER_Home, 0, + G_CALLBACK (clutter_text_real_line_start), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-start", + CLUTTER_KP_Home, 0, + G_CALLBACK (clutter_text_real_line_start), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-start", + CLUTTER_Begin, 0, + G_CALLBACK (clutter_text_real_line_start), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-end", + CLUTTER_End, 0, + G_CALLBACK (clutter_text_real_line_end), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "line-end", + CLUTTER_KP_End, 0, + G_CALLBACK (clutter_text_real_line_end), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "page-up", + CLUTTER_Page_Up, 0, + G_CALLBACK (clutter_text_real_page_up), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "page-up", + CLUTTER_KP_Page_Up, 0, + G_CALLBACK (clutter_text_real_page_up), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "page-down", + CLUTTER_Page_Down, 0, + G_CALLBACK (clutter_text_real_page_down), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "page-up", + CLUTTER_KP_Page_Down, 0, + G_CALLBACK (clutter_text_real_page_down), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "delete-next", + CLUTTER_Delete, 0, + G_CALLBACK (clutter_text_real_del_next), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-next", + CLUTTER_KP_Delete, 0, + G_CALLBACK (clutter_text_real_del_next), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-prev", + CLUTTER_BackSpace, 0, + G_CALLBACK (clutter_text_real_del_prev), + NULL, NULL); + + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_Return, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_KP_Enter, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "activate", + CLUTTER_ISO_Enter, 0, + G_CALLBACK (clutter_text_real_activate), + NULL, NULL); } static void @@ -1480,9 +1824,6 @@ clutter_text_init (ClutterText *self) priv->priv_char = '*'; priv->max_length = 0; - - init_commands (self); /* FIXME: free */ - init_mappings (self); /* FIXME: free */ } ClutterActor * @@ -1735,403 +2076,6 @@ clutter_text_get_selection_bound (ClutterText *self) return self->priv->selection_bound; } -/****************************************************************/ -/* The following are the commands available for keybinding when */ -/* using the entry, these can also be invoked programmatically */ -/* through clutter_text_action() */ -/****************************************************************/ - -static gboolean -clutter_text_action_activate (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - g_signal_emit (G_OBJECT (ttext), text_signals[ACTIVATE], 0); - return TRUE; -} - -static void -clutter_text_clear_selection (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; - - priv->selection_bound = priv->position; -} - -static gboolean -clutter_text_action_move_left (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint pos = priv->position; - gint len; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - if (pos != 0 && len !=0) - { - if (pos == -1) - { - clutter_text_set_cursor_position (ttext, len - 1); - } - else - { - clutter_text_set_cursor_position (ttext, pos - 1); - } - } - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - - -static gboolean -clutter_text_action_move_right (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint pos; - - gint len; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - pos = priv->position; - if (pos != -1 && len !=0) - { - if (pos != len) - { - clutter_text_set_cursor_position (ttext, pos + 1); - } - } - - if (!(priv->selectable && - event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - { - clutter_text_clear_selection (ttext); - } - - return TRUE; -} - -static gboolean -clutter_text_action_move_up (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - gint x; - const gchar *text; - PangoLayoutLine *layout_line; - - text = clutter_text_get_text (ttext); - - pango_layout_index_to_line_x (clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, &x); - - if (priv->x_pos != -1) - x = priv->x_pos; - else - priv->x_pos = x; - - line_no -= 1; - if (line_no < 0) - return FALSE; - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); - - if (!layout_line) - return TRUE; - - pango_layout_line_x_to_index (layout_line, x, &index_, NULL); - - { - gint pos = bytes_to_offset (text, index_); - clutter_text_set_cursor_position (ttext, pos); - } - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_down (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - gint x; - const gchar *text; - PangoLayoutLine *layout_line; - - text = clutter_text_get_text (ttext); - - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, - &x); - - if (priv->x_pos != -1) - x = priv->x_pos; - else - priv->x_pos = x; - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), - line_no + 1); - - if (!layout_line) - return FALSE; - - pango_layout_line_x_to_index (layout_line, x, &index_, NULL); - - { - gint pos = bytes_to_offset (text, index_); - - clutter_text_set_cursor_position (ttext, pos); - } - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - return TRUE; -} - -static gboolean -clutter_text_action_move_start (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - - clutter_text_set_cursor_position (ttext, 0); - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_end (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - - clutter_text_set_cursor_position (ttext, -1); - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_start_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - const gchar *text; - PangoLayoutLine *layout_line; - gint position; - - text = clutter_text_get_text (ttext); - - - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - offset_to_bytes (text, priv->position), - 0, - &line_no, - NULL); - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); - - pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); - - position = bytes_to_offset (text, index_); - clutter_text_set_cursor_position (ttext, position); - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_move_end_line (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv = ttext->priv; - gint line_no; - gint index_; - gint trailing; - const gchar *text; - PangoLayoutLine *layout_line; - gint position; - - text = clutter_text_get_text (ttext); - - index_ = offset_to_bytes (text, priv->position); - - pango_layout_index_to_line_x ( - clutter_text_get_layout (ttext), - index_, - 0, - &line_no, - NULL); - - layout_line = - pango_layout_get_line_readonly (clutter_text_get_layout (ttext), line_no); - - pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); - index_ += trailing; - - position = bytes_to_offset (text, index_); - - clutter_text_set_cursor_position (ttext, position); - - if (!(priv->selectable && event && - (event->key.modifier_state & CLUTTER_SHIFT_MASK))) - clutter_text_clear_selection (ttext); - - return TRUE; -} - -static gboolean -clutter_text_action_delete_next (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv; - gint pos; - gint len; - - if (clutter_text_truncate_selection_internal (ttext)) - return TRUE; - - priv = ttext->priv; - pos = priv->position; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - if (len && pos != -1 && pos < len) - clutter_text_delete_text (ttext, pos, pos+1); - - return TRUE; -} - -static gboolean -clutter_text_action_delete_previous (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event) -{ - ClutterTextPrivate *priv; - gint pos; - gint len; - - if (clutter_text_truncate_selection_internal (ttext)) - return TRUE; - - priv = ttext->priv; - pos = priv->position; - len = g_utf8_strlen (clutter_text_get_text (ttext), -1); - - if (pos != 0 && len !=0) - { - if (pos == -1) - { - clutter_text_set_cursor_position (ttext, len - 1); - clutter_text_set_selection_bound (ttext, len - 1); - } - else - { - clutter_text_set_cursor_position (ttext, pos - 1); - clutter_text_set_selection_bound (ttext, pos - 1); - } - clutter_text_delete_text (ttext, pos-1, pos);; - } - return TRUE; -} - -static void -init_commands (ClutterText *ttext) -{ - ClutterTextPrivate *priv = ttext->priv; - if (priv->commands) - return; - - clutter_text_add_action (ttext, "move-left", clutter_text_action_move_left); - clutter_text_add_action (ttext, "move-right", clutter_text_action_move_right); - clutter_text_add_action (ttext, "move-up", clutter_text_action_move_up); - clutter_text_add_action (ttext, "move-down", clutter_text_action_move_down); - clutter_text_add_action (ttext, "move-start", clutter_text_action_move_start); - clutter_text_add_action (ttext, "move-end", clutter_text_action_move_end); - clutter_text_add_action (ttext, "move-start-line", clutter_text_action_move_start_line); - clutter_text_add_action (ttext, "move-end-line", clutter_text_action_move_end_line); - clutter_text_add_action (ttext, "delete-previous", clutter_text_action_delete_previous); - clutter_text_add_action (ttext, "delete-next", clutter_text_action_delete_next); - clutter_text_add_action (ttext, "activate", clutter_text_action_activate); - clutter_text_add_action (ttext, "truncate-selection", clutter_text_truncate_selection); -} - -gboolean -clutter_text_action (ClutterText *ttext, - const gchar *command, - ClutterEvent *event) -{ - gchar command2[64]; - gint i; - - GList *iter; - ClutterTextPrivate *priv = ttext->priv; - - for (i=0; command[i] && - command[i]!=' '&& - i<62; i++) - { - command2[i]=command[i]; - } - command2[i]='\0'; - - if (!g_str_equal (command2, "move-up") && - !g_str_equal (command2, "move-down")) - priv->x_pos = -1; - - for (iter=priv->commands;iter;iter=iter->next) - { - TextCommand *tcommand = iter->data; - if (g_str_equal (command2, tcommand->name)) - return tcommand->func (ttext, command, event); - } - - g_warning ("unhandled text command %s", command); - return FALSE; -} - G_CONST_RETURN gchar * clutter_text_get_font_name (ClutterText *text) { @@ -3040,32 +2984,3 @@ clutter_text_get_chars (ClutterText *self, return g_strndup (priv->text + start_index, end_index - start_index); } - -void -clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline) -{ - TextMapping *tmapping = g_new (TextMapping, 1); - ClutterTextPrivate *priv = ttext->priv; - tmapping->keyval = keyval; - tmapping->state = state; - tmapping->action = commandline; - priv->mappings = g_list_append (priv->mappings, tmapping); -} - -void -clutter_text_add_action (ClutterText *ttext, - const gchar *name, - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event)) -{ - TextCommand *tcommand = g_new (TextCommand, 1); - ClutterTextPrivate *priv = ttext->priv; - tcommand->name = name; - tcommand->func = func; - priv->commands = g_list_append (priv->commands, tcommand); -} - diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 491e3a3ff..87f103486 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -167,26 +167,6 @@ void clutter_text_set_max_length (ClutterText *self gint max); gint clutter_text_get_max_length (ClutterText *self); -/* add a custom action that can be used in keybindings */ -void clutter_text_add_action (ClutterText *ttext, - const gchar *name, - gboolean (*func) (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event)); - -/* invoke an action registered by you or one of the tidy text default actions */ -gboolean clutter_text_action (ClutterText *ttext, - const gchar *commandline, - ClutterEvent *event); - -void clutter_text_mappings_clear (ClutterText *ttext); - -/* Add a keybinding to handle for the default keypress vfunc handler */ -void clutter_text_add_mapping (ClutterText *ttext, - guint keyval, - ClutterModifierType state, - const gchar *commandline); - G_END_DECLS #endif /* __CLUTTER_TEXT_H__ */ From ec4c15f8e71faee23b9b92cd49cb2a011750775b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:07:20 +0000 Subject: [PATCH 031/108] Comment out the mapping API The test-text interactive test for ClutterText should not use the mapping API, since ClutterText does not have it anymore. --- tests/interactive/test-text.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/interactive/test-text.c b/tests/interactive/test-text.c index ce262dcfc..30231b3b5 100644 --- a/tests/interactive/test-text.c +++ b/tests/interactive/test-text.c @@ -135,6 +135,7 @@ test_text_main (gint argc, clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE); clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color); +#if 0 clutter_text_add_action (CLUTTER_TEXT (text), "select-all", select_all); clutter_text_add_action (CLUTTER_TEXT (text), "copy", copy); clutter_text_add_action (CLUTTER_TEXT (text), "paste", paste); @@ -154,6 +155,7 @@ test_text_main (gint argc, CLUTTER_Page_Up, 0, "pageup"); clutter_text_add_mapping (CLUTTER_TEXT (text), CLUTTER_Page_Down, 0, "pagedown"); +#endif if (argv[1]) { From 35172a7615afa88eea20eee83634e8a07d701d5c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:08:20 +0000 Subject: [PATCH 032/108] Change the binding propagation when truncating a selection If a selection has been truncated inside a key binding handler, we should just return and let the usual key event handler continue. This fixes the case where we deleted a selection using the Delete or the Backspace keys. --- clutter/clutter-text.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 899c9a9cf..baec33d76 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1274,7 +1274,7 @@ clutter_text_real_del_next (ClutterText *self, gint len; if (clutter_text_truncate_selection (self)) - return FALSE; + return TRUE; pos = priv->position; len = g_utf8_strlen (priv->text, -1); @@ -1300,7 +1300,7 @@ clutter_text_real_del_prev (ClutterText *self, gint len; if (clutter_text_truncate_selection (self)) - return FALSE; + return TRUE; pos = priv->position; len = g_utf8_strlen (priv->text, -1); From f13e00b41189fafb2eebd3df214e19db9fb107b7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:10:46 +0000 Subject: [PATCH 033/108] Truncate selections on text insertion When inserting text on a key press event we should also truncate the selection. We should not truncate the selection when inserting any Unicode character, since changing the selection also changes the cursor position - and one of the invariants we inherited from ClutterEntry is that inserting characters programmatically does not change the cursor position. --- clutter/clutter-text.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index baec33d76..284c33d5d 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -898,7 +898,8 @@ static gboolean clutter_text_key_press (ClutterActor *actor, ClutterKeyEvent *event) { - ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; + ClutterText *self = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = self->priv; ClutterBindingPool *pool; gboolean res; gint keyval; @@ -925,7 +926,8 @@ clutter_text_key_press (ClutterActor *actor, if (g_unichar_validate (key_unichar)) { - clutter_text_insert_unichar (CLUTTER_TEXT (actor), key_unichar); + clutter_text_truncate_selection (self); + clutter_text_insert_unichar (self, key_unichar); return TRUE; } From 06b00f9dfcaa35acf01723fce0e5e224acdadba7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:13:26 +0000 Subject: [PATCH 034/108] Remove units for Entry and Label ClutterText should supercede all unit tests for ClutterLabel and ClutterEntry. --- tests/conform/Makefile.am | 5 +- tests/conform/test-clutter-entry.c | 370 ------------------ tests/conform/test-conform-main.c | 16 +- .../{test-label-cache.c => test-text-cache.c} | 42 +- 4 files changed, 24 insertions(+), 409 deletions(-) delete mode 100644 tests/conform/test-clutter-entry.c rename tests/conform/{test-label-cache.c => test-text-cache.c} (84%) diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 774156cfc..0586daf4c 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -15,15 +15,14 @@ test_conformance_SOURCES = \ test-mesh-mutability.c \ test-path.c \ test-pick.c \ - test-label-cache.c \ - test-clutter-entry.c \ test-clutter-rectangle.c \ test-clutter-fixed.c \ test-actor-invariants.c \ test-paint-opacity.c \ test-backface-culling.c \ test-binding-pool.c \ - test-clutter-text.c + test-clutter-text.c \ + test-text-cache.c # For convenience, this provides a way to easily run individual unit tests: .PHONY: wrappers diff --git a/tests/conform/test-clutter-entry.c b/tests/conform/test-clutter-entry.c deleted file mode 100644 index a12d46b75..000000000 --- a/tests/conform/test-clutter-entry.c +++ /dev/null @@ -1,370 +0,0 @@ -#include -#include -#include - -#include "test-conform-common.h" - -typedef struct { - gunichar unichar; - const char bytes[6]; - gint nbytes; -} TestData; - -static const TestData -test_data[] = { - { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ - { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ -}; - -void -test_entry_utf8_validation (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - gunichar unichar; - char bytes[6]; - int nbytes; - - g_assert (g_unichar_validate (t->unichar)); - - nbytes = g_unichar_to_utf8 (t->unichar, bytes); - bytes[nbytes] = '\0'; - g_assert (nbytes == t->nbytes); - g_assert (memcmp (t->bytes, bytes, nbytes) == 0); - - unichar = g_utf8_get_char_validated (bytes, nbytes); - g_assert (unichar == t->unichar); - } -} - -static int -get_nbytes (ClutterEntry *entry) -{ - const char *s = clutter_entry_get_text (entry); - return strlen (s); -} - -static int -get_nchars (ClutterEntry *entry) -{ - const char *s = clutter_entry_get_text (entry); - g_assert (g_utf8_validate (s, -1, NULL)); - return g_utf8_strlen (s, -1); -} - -#define DONT_MOVE_CURSOR (-2) - -static void -insert_unichar (ClutterEntry *entry, gunichar unichar, int position) -{ - if (position > DONT_MOVE_CURSOR) - { - clutter_entry_set_cursor_position (entry, position); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, position); - } - - clutter_entry_insert_unichar (entry, unichar); -} - -void -test_entry_empty (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - g_assert (clutter_entry_get_text (entry) == NULL); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_set_empty (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - /* annoyingly slightly different from initially empty */ - clutter_entry_set_text (entry, ""); - g_assert_cmpint (get_nchars (entry), ==, 0); - g_assert_cmpint (get_nbytes (entry), ==, 0); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_set_text (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - - clutter_entry_set_text (entry, "abcdef"); - g_assert_cmpint (get_nchars (entry), ==, 6); - g_assert_cmpint (get_nbytes (entry), ==, 6); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_entry_set_cursor_position (entry, 5); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 5); - - clutter_entry_set_text (entry, ""); - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_append_some (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 1; j <= 4; j++) - { - insert_unichar (entry, t->unichar, DONT_MOVE_CURSOR); - g_assert_cmpint (get_nchars (entry), ==, j); - g_assert_cmpint (get_nbytes (entry), ==, j * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - } - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_prepend_some (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - clutter_entry_insert_unichar (entry, t->unichar); - g_assert_cmpint (get_nchars (entry), ==, 1); - g_assert_cmpint (get_nbytes (entry), ==, 1 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - for (j = 2; j <= 4; j++) - { - insert_unichar (entry, t->unichar, 0); - g_assert_cmpint (get_nchars (entry), ==, j); - g_assert_cmpint (get_nbytes (entry), ==, j * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - } - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_insert (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - - clutter_entry_insert_unichar (entry, t->unichar); - clutter_entry_insert_unichar (entry, t->unichar); - - insert_unichar (entry, t->unichar, 1); - g_assert_cmpint (get_nchars (entry), ==, 3); - g_assert_cmpint (get_nbytes (entry), ==, 3 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 2); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_delete_chars (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; j++) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 2); - clutter_entry_delete_chars (entry, 1); - g_assert_cmpint (get_nchars (entry), ==, 3); - g_assert_cmpint (get_nbytes (entry), ==, 3 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - clutter_entry_set_cursor_position (entry, 2); - clutter_entry_delete_chars (entry, 1); - g_assert_cmpint (get_nchars (entry), ==, 2); - g_assert_cmpint (get_nbytes (entry), ==, 2 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_delete_text (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; j++) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 3); - clutter_entry_delete_text (entry, 2, 4); - - g_assert_cmpint (get_nchars (entry), ==, 2); - g_assert_cmpint (get_nbytes (entry), ==, 2 * t->nbytes); - - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -static void -init_event (ClutterKeyEvent *event) -{ - event->type = CLUTTER_KEY_PRESS; - event->time = 0; /* not needed */ - event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC; - event->stage = NULL; /* not needed */ - event->source = NULL; /* not needed */ - event->modifier_state = 0; - event->hardware_keycode = 0; /* not needed */ -} - -static void -send_keyval (ClutterEntry *entry, int keyval) -{ - ClutterKeyEvent event; - - init_event (&event); - event.keyval = keyval; - event.unicode_value = 0; /* should be ignored for cursor keys etc. */ - - clutter_entry_handle_key_event (entry, &event); -} - -static inline void -send_unichar (ClutterEntry *entry, gunichar unichar) -{ - ClutterKeyEvent event; - - init_event (&event); - event.keyval = 0; /* should be ignored for printable characters */ - event.unicode_value = unichar; - - clutter_entry_handle_key_event (entry, &event); -} - -void -test_entry_cursor (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - int j; - - for (j = 0; j < 4; ++j) - clutter_entry_insert_unichar (entry, t->unichar); - - clutter_entry_set_cursor_position (entry, 2); - - /* test cursor moves and is clamped */ - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 1); - - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 0); - - send_keyval (entry, CLUTTER_Left); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 0); - - /* delete text containing the cursor */ - clutter_entry_set_cursor_position (entry, 3); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, 3); - - clutter_entry_delete_text (entry, 2, 4); - send_keyval (entry, CLUTTER_Left); - - /* FIXME: cursor position should be -1? - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - */ - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - -void -test_entry_event (TestConformSimpleFixture *fixture, - gconstpointer data) -{ - ClutterEntry *entry = CLUTTER_ENTRY (clutter_entry_new ()); - int i; - - for (i = 0; i < G_N_ELEMENTS (test_data); i++) - { - const TestData *t = &test_data[i]; - - send_unichar (entry, t->unichar); - - g_assert_cmpint (get_nchars (entry), ==, 1); - g_assert_cmpint (get_nbytes (entry), ==, 1 * t->nbytes); - g_assert_cmpint (clutter_entry_get_cursor_position (entry), ==, -1); - - clutter_entry_set_text (entry, ""); - } - - clutter_actor_destroy (CLUTTER_ACTOR (entry)); -} - diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index d12c4b83a..abf6280b2 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -68,21 +68,6 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/picking", test_pick); - TEST_CONFORM_SIMPLE ("/label", test_label_cache); - - /* ClutterEntry */ - TEST_CONFORM_SIMPLE ("/entry", test_entry_utf8_validation); - TEST_CONFORM_SIMPLE ("/entry", test_entry_empty); - TEST_CONFORM_SIMPLE ("/entry", test_entry_set_empty); - TEST_CONFORM_SIMPLE ("/entry", test_entry_set_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_append_some); - TEST_CONFORM_SIMPLE ("/entry", test_entry_prepend_some); - TEST_CONFORM_SIMPLE ("/entry", test_entry_insert); - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_chars); - TEST_CONFORM_SIMPLE ("/entry", test_entry_delete_text); - TEST_CONFORM_SIMPLE ("/entry", test_entry_cursor); - TEST_CONFORM_SIMPLE ("/entry", test_entry_event); - /* ClutterText */ TEST_CONFORM_SIMPLE ("/text", test_text_utf8_validation); TEST_CONFORM_SIMPLE ("/text", test_text_empty); @@ -96,6 +81,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/text", test_text_cursor); TEST_CONFORM_SIMPLE ("/text", test_text_event); TEST_CONFORM_SIMPLE ("/text", test_text_get_chars); + TEST_CONFORM_SIMPLE ("/text", test_text_cache); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); diff --git a/tests/conform/test-label-cache.c b/tests/conform/test-text-cache.c similarity index 84% rename from tests/conform/test-label-cache.c rename to tests/conform/test-text-cache.c index e9298f8fd..26687fc00 100644 --- a/tests/conform/test-label-cache.c +++ b/tests/conform/test-text-cache.c @@ -34,7 +34,7 @@ on_paint (ClutterActor *label, CallbackData *data) /* Check whether the layout used for this paint is different from the layout used for the last paint */ - new_layout = clutter_label_get_layout (CLUTTER_LABEL (data->label)); + new_layout = clutter_text_get_layout (CLUTTER_TEXT (data->label)); data->layout_changed = data->old_layout != new_layout; if (data->old_layout) @@ -119,12 +119,12 @@ do_tests (CallbackData *data) PangoAttribute *attr; /* TEST 1: change the text */ - clutter_label_set_text (CLUTTER_LABEL (data->label), "Counter 0"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 0"); pango_layout_set_text (data->test_layout, "Counter 0", -1); check_result (data, "Change text", TRUE); /* TEST 2: change a single character */ - clutter_label_set_text (CLUTTER_LABEL (data->label), "Counter 1"); + clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 1"); pango_layout_set_text (data->test_layout, "Counter 1", -1); check_result (data, "Change a single character", TRUE); @@ -133,14 +133,14 @@ do_tests (CallbackData *data) check_result (data, "Move the label", FALSE); /* TEST 4: change the font */ - clutter_label_set_font_name (CLUTTER_LABEL (data->label), "Serif 15"); + clutter_text_set_font_name (CLUTTER_TEXT (data->label), "Serif 15"); fd = pango_font_description_from_string ("Serif 15"); pango_layout_set_font_description (data->test_layout, fd); pango_font_description_free (fd); check_result (data, "Change the font", TRUE); /* TEST 5: change the color */ - clutter_label_set_color (CLUTTER_LABEL (data->label), &red); + clutter_text_set_color (CLUTTER_TEXT (data->label), &red); check_result (data, "Change the color", FALSE); /* TEST 6: change the attributes */ @@ -150,21 +150,21 @@ do_tests (CallbackData *data) attr_list = pango_attr_list_new (); pango_attr_list_insert (attr_list, attr); attr_list_copy = pango_attr_list_copy (attr_list); - clutter_label_set_attributes (CLUTTER_LABEL (data->label), attr_list); + clutter_text_set_attributes (CLUTTER_TEXT (data->label), attr_list); pango_layout_set_attributes (data->test_layout, attr_list_copy); pango_attr_list_unref (attr_list_copy); pango_attr_list_unref (attr_list); check_result (data, "Change the attributes", TRUE); /* TEST 7: change the text again */ - clutter_label_set_attributes (CLUTTER_LABEL (data->label), NULL); - clutter_label_set_text (CLUTTER_LABEL (data->label), long_text); + clutter_text_set_attributes (CLUTTER_TEXT (data->label), NULL); + clutter_text_set_text (CLUTTER_TEXT (data->label), long_text); pango_layout_set_attributes (data->test_layout, NULL); pango_layout_set_text (data->test_layout, long_text, -1); check_result (data, "Change the text again", TRUE); /* TEST 8: enable markup */ - clutter_label_set_use_markup (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_use_markup (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_markup (data->test_layout, long_text, -1); check_result (data, "Enable markup", TRUE); @@ -178,28 +178,28 @@ do_tests (CallbackData *data) force_redraw (data); /* TEST 9: enable ellipsize */ - clutter_label_set_ellipsize (CLUTTER_LABEL (data->label), + clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_END); check_result (data, "Enable ellipsize", TRUE); - clutter_label_set_ellipsize (CLUTTER_LABEL (data->label), + clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_NONE); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_NONE); force_redraw (data); /* TEST 10: enable line wrap */ - clutter_label_set_line_wrap (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_line_wrap (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_WORD); check_result (data, "Enable line wrap", TRUE); /* TEST 11: change wrap mode */ - clutter_label_set_line_wrap_mode (CLUTTER_LABEL (data->label), + clutter_text_set_line_wrap_mode (CLUTTER_TEXT (data->label), PANGO_WRAP_CHAR); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_CHAR); check_result (data, "Change wrap mode", TRUE); /* TEST 12: enable justify */ - clutter_label_set_justify (CLUTTER_LABEL (data->label), TRUE); + clutter_text_set_justify (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_justify (data->test_layout, TRUE); /* Pango appears to have a bug which means that you can't change the justification after setting the text but this fixes it. @@ -208,7 +208,7 @@ do_tests (CallbackData *data) check_result (data, "Enable justify", TRUE); /* TEST 13: change alignment */ - clutter_label_set_alignment (CLUTTER_LABEL (data->label), PANGO_ALIGN_RIGHT); + clutter_text_set_alignment (CLUTTER_TEXT (data->label), PANGO_ALIGN_RIGHT); pango_layout_set_alignment (data->test_layout, PANGO_ALIGN_RIGHT); check_result (data, "Change alignment", TRUE); @@ -218,7 +218,7 @@ do_tests (CallbackData *data) } static PangoLayout * -make_layout_like_label (ClutterLabel *label) +make_layout_like_label (ClutterText *label) { PangoLayout *label_layout, *new_layout; PangoContext *context; @@ -226,7 +226,7 @@ make_layout_like_label (ClutterLabel *label) /* Make another layout using the same context as the layout from the label */ - label_layout = clutter_label_get_layout (label); + label_layout = clutter_text_get_layout (label); context = pango_layout_get_context (label_layout); new_layout = pango_layout_new (context); fd = pango_font_description_from_string (TEST_FONT); @@ -237,8 +237,8 @@ make_layout_like_label (ClutterLabel *label) } void -test_label_cache (TestConformSimpleFixture *fixture, - gconstpointer _data) +test_text_cache (TestConformSimpleFixture *fixture, + gconstpointer _data) { CallbackData data; @@ -246,9 +246,9 @@ test_label_cache (TestConformSimpleFixture *fixture, data.stage = clutter_stage_get_default (); - data.label = clutter_label_new_with_text (TEST_FONT, ""); + data.label = clutter_text_new_with_text (TEST_FONT, ""); - data.test_layout = make_layout_like_label (CLUTTER_LABEL (data.label)); + data.test_layout = make_layout_like_label (CLUTTER_TEXT (data.label)); g_signal_connect (data.label, "paint", G_CALLBACK (on_paint), &data); From 7a93bcb64eef54c1fae50129111a2d810ad4f622 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:14:53 +0000 Subject: [PATCH 035/108] Remove unused types Since ClutterText now uses ClutterBindingPool there is no more need for the internal key mapping types. --- clutter/clutter-text.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 284c33d5d..dcd6ec231 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -57,8 +57,6 @@ #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) typedef struct _LayoutCache LayoutCache; -typedef struct _TextCommand TextCommand; -typedef struct _TextMapping TextMapping; /* Probably move into main */ static PangoContext *_context = NULL; From cfb60228313d16c3a373e8d39b6c05e310ab4ef4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:15:49 +0000 Subject: [PATCH 036/108] Code style fixes Fix the arguments alignment whenever not consistent with the coding style rules. --- clutter/clutter-text.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index dcd6ec231..db5432896 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1327,10 +1327,10 @@ clutter_text_real_del_prev (ClutterText *self, } static gboolean -clutter_text_real_activate (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) +clutter_text_real_activate (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; @@ -1345,19 +1345,19 @@ clutter_text_real_activate (ClutterText *self, } static gboolean -clutter_text_real_page_up (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) +clutter_text_real_page_up (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) { return FALSE; } static gboolean -clutter_text_real_page_down (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) +clutter_text_real_page_down (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) { return FALSE; } From e9c369f86d0eea327458cc4bd7b9bc750960a09f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:17:11 +0000 Subject: [PATCH 037/108] Add documentation annotations ClutterText is heavily under-documented. This commit ports most of the API documentation from ClutterLabel and ClutterEntry to ClutterText. --- clutter/clutter-text.c | 192 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index db5432896..886256844 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1648,7 +1648,7 @@ clutter_text_class_init (ClutterTextClass *klass) /** * ClutterText::text-changed: - * @actor: the actor which received the event + * @self: the #ClutterText that emitted the signal * * The ::text-changed signal is emitted after @actor's text changes * @@ -1663,6 +1663,15 @@ clutter_text_class_init (ClutterTextClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ClutterText::cursor-event: + * @self: the #ClutterText that emitted the signal + * @geometry: the coordinates of the cursor + * + * FIXME + * + * Since: 1.0 + */ text_signals[CURSOR_EVENT] = g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), @@ -1675,7 +1684,7 @@ clutter_text_class_init (ClutterTextClass *klass) /** * ClutterText::activate - * @actor: the actor which received the event + * @self: the #ClutterText that emitted the signal * * The ::activate signal is emitted each time the actor is 'activated' * by the user, normally by pressing the 'Enter' key. @@ -2076,6 +2085,18 @@ clutter_text_get_selection_bound (ClutterText *self) return self->priv->selection_bound; } +/** + * clutter_text_get_font_name: + * @self: a #ClutterText + * + * Retrieves the font name as set by clutter_text_set_font_name(). + * + * Return value: a string containing the font name. The returned + * string is owned by the #ClutterText actor and should not be + * modified or freed + * + * Since: 1.0 + */ G_CONST_RETURN gchar * clutter_text_get_font_name (ClutterText *text) { @@ -2084,6 +2105,23 @@ clutter_text_get_font_name (ClutterText *text) return text->priv->font_name; } +/** + * clutter_text_set_font_name: + * @self: a #ClutterText + * @font_name: a font name + * + * Sets the font used by a #ClutterText. The @font_name string + * must be something that can be parsed by the + * pango_font_description_from_string() function, like: + * + * |[ + * clutter_text_set_font_name (text, "Sans 10pt"); + * clutter_text_set_font_name (text, "Serif 16px"); + * clutter_text_set_font_name (text, "Helvetica 10"); + * ]| + * + * Since: 1.0 + */ void clutter_text_set_font_name (ClutterText *self, const gchar *font_name) @@ -2126,6 +2164,28 @@ clutter_text_set_font_name (ClutterText *self, g_object_notify (G_OBJECT (self), "font-name"); } +/** + * clutter_text_get_text: + * @self: a #ClutterText + * + * Retrieves a pointer to the current contents of a #ClutterText + * actor. + * + * If you need a copy of the contents for manipulating, either + * use g_strdup() on the returned string, or use: + * + * |[ + * copy = clutter_text_get_chars (text, 0, -1); + * ]| + * + * Which will return a newly allocated string. + * + * Return value: the contents of the actor. The returned string + * is owned by the #ClutterText actor and should never be + * modified or freed + * + * Since: 1.0 + */ G_CONST_RETURN gchar * clutter_text_get_text (ClutterText *self) { @@ -2134,6 +2194,15 @@ clutter_text_get_text (ClutterText *self) return self->priv->text; } +/** + * clutter_text_set_text: + * @self: a #ClutterText + * @text: the text to set + * + * Sets the contents of a #ClutterText actor. + * + * Since: 1.0 + */ void clutter_text_set_text (ClutterText *self, const gchar *text) @@ -2188,6 +2257,17 @@ clutter_text_set_text (ClutterText *self, g_object_notify (G_OBJECT (self), "text"); } +/** + * clutter_text_get_layout: + * @self: a #ClutterText + * + * Retrieves the current #PangoLayout used by a #ClutterText actor. + * + * Return value: a #PangoLayout. The returned object is owned by + * the #ClutterText actor and should not be modified or freed + * + * Since: 1.0 + */ PangoLayout * clutter_text_get_layout (ClutterText *self) { @@ -2200,6 +2280,20 @@ clutter_text_get_layout (ClutterText *self) return clutter_text_create_layout (self, width); } +/** + * clutter_text_set_color: + * @self: a #ClutterText + * @color: a #ClutterColor + * + * Sets the color of the contents of a #ClutterText actor. + * + * The overall opacity of the #ClutterText actor will be the + * result of the alpha value of @color and the composited + * opacity of the actor itself on the scenegraph, as returned + * by clutter_actor_get_paint_opacity(). + * + * Since: 1.0 + */ void clutter_text_set_color (ClutterText *self, const ClutterColor *color) @@ -2219,6 +2313,15 @@ clutter_text_set_color (ClutterText *self, g_object_notify (G_OBJECT (self), "color"); } +/** + * clutter_text_get_color: + * @self: a #ClutterText + * @color: return location for a #ClutterColor + * + * Retrieves the text color as set by clutter_text_get_color(). + * + * Since: 1.0 + */ void clutter_text_get_color (ClutterText *self, ClutterColor *color) @@ -2287,6 +2390,17 @@ clutter_text_get_ellipsize (ClutterText *self) return self->priv->ellipsize; } +/** + * clutter_text_get_line_wrap: + * @self: a #ClutterText + * + * Retrieves the value set using clutter_text_set_line_wrap(). + * + * Return value: %TRUE if the #ClutterText actor should wrap + * its contents + * + * Since: 1.0 + */ gboolean clutter_text_get_line_wrap (ClutterText *self) { @@ -2295,6 +2409,16 @@ clutter_text_get_line_wrap (ClutterText *self) return self->priv->wrap; } +/** + * clutter_text_set_line_wrap: + * @self: a #ClutterText + * @line_wrap: whether the contents should wrap + * + * Sets whether the contents of a #ClutterText actor should wrap, + * if they don't fit the size assigned to the actor. + * + * Since: 1.0 + */ void clutter_text_set_line_wrap (ClutterText *self, gboolean line_wrap) @@ -2555,7 +2679,7 @@ clutter_text_get_use_markup (ClutterText *self) * on both margins. This setting is ignored if Clutter is compiled * against Pango < 1.18. * - * Since: 0.6 + * Since: 1.0 */ void clutter_text_set_justify (ClutterText *self, @@ -2616,6 +2740,17 @@ clutter_text_get_cursor_position (ClutterText *self) return self->priv->position; } +/** + * clutter_text_set_cursor_position: + * @self: a #ClutterText + * @position: the new cursor position, in characters + * + * Sets the cursor of a #ClutterText actor at @position. + * + * The position is expressed in characters, not in bytes. + * + * Since: 1.0 + */ void clutter_text_set_cursor_position (ClutterText *self, gint position) @@ -2805,6 +2940,16 @@ clutter_text_get_max_length (ClutterText *self) return self->priv->max_length; } +/** + * clutter_text_insert_unichar: + * @self: a #ClutterText + * @wc: a Unicode character + * + * Inserts @wc at the current cursor position of a + * #ClutterText actor. + * + * Since: 1.0 + */ void clutter_text_insert_unichar (ClutterText *self, gunichar wc) @@ -2841,6 +2986,21 @@ clutter_text_insert_unichar (ClutterText *self, g_string_free (new, TRUE); } +/** + * clutter_text_insert_text: + * @self: a #ClutterText + * @text: the text to be inserted + * @position: the position of the insertion, or -1 + * + * Inserts @text into a #ClutterActor at the given position. + * + * If @position is a negative number, the text will be appended + * at the end of the current contents of the #ClutterText. + * + * The position is expressed in characters, not in bytes. + * + * Since: 1.0 + */ void clutter_text_insert_text (ClutterText *self, const gchar *text, @@ -2862,6 +3022,20 @@ clutter_text_insert_text (ClutterText *self, g_string_free (new, TRUE); } +/** + * clutter_text_delete_text: + * @self: a #ClutterText + * @start_pos: starting position + * @end_pos: ending position + * + * Deletes the text inside a #ClutterText actor between @start_pos + * and @end_pos. + * + * The starting and ending positions are expressed in characters, + * not in bytes. + * + * Since: 1.0 + */ void clutter_text_delete_text (ClutterText *self, gssize start_pos, @@ -2900,6 +3074,16 @@ clutter_text_delete_text (ClutterText *self, g_string_free (new, TRUE); } +/** + * clutter_text_delete_chars: + * @self: a #ClutterText + * @n_chars: the number of characters to delete + * + * Deletes @n_chars inside a #ClutterText actor, starting from the + * current cursor position. + * + * Since: 1.0 + */ void clutter_text_delete_chars (ClutterText *self, guint n_chars) @@ -2944,7 +3128,7 @@ clutter_text_delete_chars (ClutterText *self, /** * clutter_text_get_chars: - * @self: a #ClutterTex + * @self: a #ClutterText * @start_pos: start of text, in characters * @end_pos: end of text, in characters * From 2ca7cb46f95ec49e1016ddb880f2fd9ff6cc8c25 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:18:26 +0000 Subject: [PATCH 038/108] Use the paint opacity when painting a ClutterText ClutterText should use the paint opacity for both text and cursor. ClutterLabel had the wrong behaviour, as it set the actor's opacity using the text color's alpha channel, and ClutterEntry completely disregarded the actor's opacity when painting the cursor. This commit harmonizes the ClutterText behaviour to always use a composition of the actor's paint opacity and the text and cursor alpha channel values, thus behaving more consistently with the rest of Clutter. --- clutter/clutter-text.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 886256844..8027dfecc 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -719,22 +719,32 @@ static void cursor_paint (ClutterText *self) { ClutterTextPrivate *priv = self->priv; + ClutterActor *actor = CLUTTER_ACTOR (self); + guint8 real_opacity; if (priv->editable && priv->cursor_visible) { if (priv->cursor_color_set) { + real_opacity = clutter_actor_get_paint_opacity (actor) + * priv->cursor_color.alpha + / 255; + cogl_set_source_color4ub (priv->cursor_color.red, priv->cursor_color.green, priv->cursor_color.blue, - priv->cursor_color.alpha); + real_opacity); } else { + real_opacity = clutter_actor_get_paint_opacity (actor) + * priv->text_color.alpha + / 255; + cogl_set_source_color4ub (priv->text_color.red, priv->text_color.green, priv->text_color.blue, - priv->text_color.alpha); + real_opacity); } clutter_text_ensure_cursor_position (self); @@ -942,6 +952,7 @@ clutter_text_paint (ClutterActor *self) PangoLayout *layout; ClutterActorBox alloc = { 0, }; CoglColor color = { 0, }; + guint8 real_opacity; if (priv->font_desc == NULL || priv->text == NULL) { @@ -958,11 +969,15 @@ clutter_text_paint (ClutterActor *self) clutter_actor_get_allocation_box (self, &alloc); layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + real_opacity = clutter_actor_get_paint_opacity (self) + * priv->text_color.alpha + / 255; + cogl_color_set_from_4ub (&color, priv->text_color.red, priv->text_color.green, priv->text_color.blue, - clutter_actor_get_paint_opacity (self)); + real_opacity); cogl_pango_render_layout (layout, 0, 0, &color, 0); } From 77b3b9d5f494bb193611004456dd8f025e379c18 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:21:57 +0000 Subject: [PATCH 039/108] Do not use contractions when not needed I understand we are not Pascal developers, and we don't have to use cute and cuddly names like "i_am_an_integer_counter", but a ClutterButtonEvent should be stored inside an "event" variable. Using "bev" instead? Mmmh, not so much. --- clutter/clutter-text.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 8027dfecc..4a40a8bf0 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -821,15 +821,15 @@ cursor_paint (ClutterText *self) static gboolean clutter_text_button_press (ClutterActor *actor, - ClutterButtonEvent *bev) + ClutterButtonEvent *event) { ClutterText *self = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = self->priv; ClutterUnit x, y; gint index_; - x = CLUTTER_UNITS_FROM_INT (bev->x); - y = CLUTTER_UNITS_FROM_INT (bev->y); + x = CLUTTER_UNITS_FROM_INT (event->x); + y = CLUTTER_UNITS_FROM_INT (event->y); clutter_actor_transform_stage_point (actor, x, y, &x, &y); From 786bc4d4d5741b799d7da2a86cd584600a3a2993 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:24:45 +0000 Subject: [PATCH 040/108] Allow only synthetic events with keyval 0 We allow KeyEvents with a key symbol of '0' to fall through only if they are marked as synthetic. Otherwise we discard them without mercy. Synthetic events are useful to test ClutterText behaviour; in fact, we do use them inside the test suite exactly for that reason. --- clutter/clutter-text.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4a40a8bf0..4b657d1da 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -920,20 +920,36 @@ clutter_text_key_press (ClutterActor *actor, pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); g_assert (pool != NULL); - res = clutter_binding_pool_activate (pool, keyval, - event->modifier_state, - G_OBJECT (actor)); + /* we allow passing synthetic events that only contain + * the Unicode value and not the key symbol + */ + if (keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC)) + res = FALSE; + else + res = clutter_binding_pool_activate (pool, keyval, + event->modifier_state, + G_OBJECT (actor)); + + /* if the key binding has handled the event we bail out + * as fast as we can; otherwise, we try to insert the + * Unicode character inside the key event into the text + * actor + */ if (res) return TRUE; else { gunichar key_unichar = clutter_key_event_unicode (event); - if (key_unichar == '\r') /* return is reported as CR we want LF */ + /* return is reported as CR, but we want LF */ + if (key_unichar == '\r') key_unichar = '\n'; if (g_unichar_validate (key_unichar)) { + /* truncate the eventual selection so that the + * Unicode character can replace it + */ clutter_text_truncate_selection (self); clutter_text_insert_unichar (self, key_unichar); From edd6b65743132648172b2bfa99eb69be82fd1971 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 12:26:47 +0000 Subject: [PATCH 041/108] Update the paint opacity unit test ClutterText behaviour with regards to the paint opacity has been changed by commit 07e19fff5ffa9617413fa6c3715914513fec3793. We need to update the test suite for the paint opacity to reflect that change as well. --- tests/conform/test-paint-opacity.c | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/conform/test-paint-opacity.c b/tests/conform/test-paint-opacity.c index 77618a98e..3dd48e912 100644 --- a/tests/conform/test-paint-opacity.c +++ b/tests/conform/test-paint-opacity.c @@ -14,12 +14,12 @@ test_label_opacity (TestConformSimpleFixture *fixture, stage = clutter_stage_get_default (); - label = clutter_label_new_with_text ("Sans 18px", "Label, 50% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text ("Sans 18px", "Label, 50% opacity"); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_container_add (CLUTTER_CONTAINER (stage), label, NULL); @@ -27,12 +27,16 @@ test_label_opacity (TestConformSimpleFixture *fixture, if (g_test_verbose ()) g_print ("label 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) - g_print ("label 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); + g_print ("label 50%%.get_paint_opacity()/1\n"); + g_assert (clutter_actor_get_paint_opacity (label) == 255); + + if (g_test_verbose ()) + g_print ("label 50%%.get_paint_opacity()/2\n"); + clutter_actor_set_opacity (label, 128); g_assert (clutter_actor_get_paint_opacity (label) == 128); clutter_actor_destroy (label); @@ -66,8 +70,7 @@ test_rectangle_opacity (TestConformSimpleFixture *fixture, g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); + g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 255); clutter_actor_destroy (rect); @@ -91,25 +94,24 @@ test_paint_opacity (TestConformSimpleFixture *fixture, clutter_actor_set_position (group1, 10, 30); clutter_actor_show (group1); - label = clutter_label_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/1\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_container_add (CLUTTER_CONTAINER (group1), label, NULL); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/2\n"); - clutter_label_get_color (CLUTTER_LABEL (label), &color_check); + clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) - g_print ("label 50%% + group 50%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (label)); - g_assert (clutter_actor_get_paint_opacity (label) == 64); + g_print ("label 50%% + group 50%%.get_paint_opacity() = 128\n"); + g_assert (clutter_actor_get_paint_opacity (label) == 128); clutter_actor_destroy (label); @@ -133,9 +135,7 @@ test_paint_opacity (TestConformSimpleFixture *fixture, g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) - g_print ("rect 100%%.get_paint_opacity() = %d\n", - clutter_actor_get_paint_opacity (rect)); - + g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 128); clutter_actor_destroy (rect); From 45de6df615c5573c38d635fd95c1bb06066e2c82 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:39:35 +0000 Subject: [PATCH 042/108] Update ignore file Add the conformance test units for ClutterText. --- .gitignore | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index dba281eaa..11ca7ca69 100644 --- a/.gitignore +++ b/.gitignore @@ -152,6 +152,19 @@ stamp-h1 /tests/conform/test_rectangle_opacity /tests/conform/test_backface_culling /tests/conform/test_binding_pool +/tests/conform/test_text_append_some +/tests/conform/test_text_cache +/tests/conform/test_text_cursor +/tests/conform/test_text_delete_chars +/tests/conform/test_text_delete_text +/tests/conform/test_text_empty +/tests/conform/test_text_event +/tests/conform/test_text_get_chars +/tests/conform/test_text_insert +/tests/conform/test_text_prepend_some +/tests/conform/test_text_set_empty +/tests/conform/test_text_set_text +/tests/conform/test_text_utf8_validation /tests/micro-bench/test-text /tests/tools/disable-npots.sh /clutter/x11/clutter-x11-enum-types.[ch] From 24a616368845de3b2e1ee4c335f3051411dfecd6 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:45:22 +0000 Subject: [PATCH 043/108] Do not compile the opacity and entry interactive tests The test-opacity interactive test is superceded by the equivalent units inside the conformance test suite. The test-entry interactive test is superceded by the test-text one. --- tests/interactive/Makefile.am | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 3a536162d..c07f48fa4 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -6,7 +6,6 @@ UNIT_TESTS = \ test-scale.c \ test-actors.c \ test-behave.c \ - test-entry.c \ test-project.c \ test-perspective.c \ test-rotate.c \ @@ -21,7 +20,6 @@ UNIT_TESTS = \ test-shader.c \ test-unproject.c \ test-viewport test-fbo.c \ - test-opacity.c \ test-multistage.c \ test-cogl-primitives.c \ test-cogl-tex-tile.c \ From 5c14044e5216274cd806122e9ac2550ebb39d9bf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:48:01 +0000 Subject: [PATCH 044/108] Update the interactive tests to ClutterText Instead of using ClutterLabel, use ClutterText to display some text where needed. --- tests/interactive/test-clip.c | 4 +-- tests/interactive/test-cogl-tex-polygon.c | 14 ++++----- tests/interactive/test-depth.c | 10 +++---- tests/interactive/test-easing.c | 8 +++--- tests/interactive/test-fbo.c | 4 +-- tests/interactive/test-layout.c | 4 +-- tests/interactive/test-multistage.c | 8 +++--- tests/interactive/test-project.c | 4 +-- tests/interactive/test-random-text.c | 2 +- tests/interactive/test-rotate.c | 4 +-- tests/interactive/test-shader.c | 2 +- tests/interactive/test-stage-read-pixels.c | 6 ++-- tests/interactive/test-unproject.c | 33 +++++++++++----------- 13 files changed, 51 insertions(+), 52 deletions(-) diff --git a/tests/interactive/test-clip.c b/tests/interactive/test-clip.c index 3b52fdb81..1d3d05a33 100644 --- a/tests/interactive/test-clip.c +++ b/tests/interactive/test-clip.c @@ -305,8 +305,8 @@ test_clip_main (int argc, char **argv) data.hand = cogl_texture_new_from_file ("redhand.png", 64, FALSE, COGL_PIXEL_FORMAT_ANY, NULL); - label = clutter_label_new_with_text ("Sans 12px", instructions); - clutter_label_set_line_wrap (CLUTTER_LABEL (label), TRUE); + label = clutter_text_new_with_text ("Sans 12px", instructions); + clutter_text_set_line_wrap (CLUTTER_TEXT (label), TRUE); clutter_actor_set_width (label, clutter_actor_get_width (data.stage) - 310); clutter_actor_set_y (label, clutter_actor_get_height (data.stage) diff --git a/tests/interactive/test-cogl-tex-polygon.c b/tests/interactive/test-cogl-tex-polygon.c index 5e237eda7..1e9d38b18 100644 --- a/tests/interactive/test-cogl-tex-polygon.c +++ b/tests/interactive/test-cogl-tex-polygon.c @@ -297,16 +297,16 @@ frame_cb (ClutterTimeline *timeline, } static void -update_toggle_text (ClutterLabel *button, gboolean val) +update_toggle_text (ClutterText *button, gboolean val) { - clutter_label_set_text (button, val ? "Enabled" : "Disabled"); + clutter_text_set_text (button, val ? "Enabled" : "Disabled"); } static gboolean on_toggle_click (ClutterActor *button, ClutterEvent *event, gboolean *toggle_val) { - update_toggle_text (CLUTTER_LABEL (button), *toggle_val = !*toggle_val); + update_toggle_text (CLUTTER_TEXT (button), *toggle_val = !*toggle_val); return TRUE; } @@ -315,12 +315,12 @@ static ClutterActor * make_toggle (const char *label_text, gboolean *toggle_val) { ClutterActor *group = clutter_group_new (); - ClutterActor *label = clutter_label_new_with_text ("Sans 14", label_text); - ClutterActor *button = clutter_label_new_with_text ("Sans 14", ""); + ClutterActor *label = clutter_text_new_with_text ("Sans 14", label_text); + ClutterActor *button = clutter_text_new_with_text ("Sans 14", ""); clutter_actor_set_reactive (button, TRUE); - update_toggle_text (CLUTTER_LABEL (button), *toggle_val); + update_toggle_text (CLUTTER_TEXT (button), *toggle_val); clutter_actor_set_position (button, clutter_actor_get_width (label) + 10, 0); clutter_container_add (CLUTTER_CONTAINER (group), label, button, NULL); @@ -373,7 +373,7 @@ test_cogl_tex_polygon_main (int argc, char *argv[]) clutter_actor_set_position (filtering_toggle, 0, clutter_actor_get_y (slicing_toggle) - clutter_actor_get_height (filtering_toggle)); - note = clutter_label_new_with_text ("Sans 10", "<- Click to change"); + note = clutter_text_new_with_text ("Sans 10", "<- Click to change"); clutter_actor_set_position (note, clutter_actor_get_width (filtering_toggle) + 10, (clutter_actor_get_height (stage) diff --git a/tests/interactive/test-depth.c b/tests/interactive/test-depth.c index a07eff234..02e5e8bce 100644 --- a/tests/interactive/test-depth.c +++ b/tests/interactive/test-depth.c @@ -83,10 +83,10 @@ janus_group (const gchar *front_text, group = clutter_group_new (); rectangle = clutter_rectangle_new_with_color (&slide_color); - front = clutter_label_new_with_text ("Sans 50px", front_text); - back = clutter_label_new_with_text ("Sans 50px", back_text); - clutter_label_set_color (CLUTTER_LABEL (front), &red); - clutter_label_set_color (CLUTTER_LABEL (back), &green); + front = clutter_text_new_with_text ("Sans 50px", front_text); + back = clutter_text_new_with_text ("Sans 50px", back_text); + clutter_text_set_color (CLUTTER_TEXT (front), &red); + clutter_text_set_color (CLUTTER_TEXT (back), &green); clutter_actor_get_size (front, &width, &height); clutter_actor_get_size (back, &width2, &height2); @@ -134,7 +134,7 @@ test_depth_main (int argc, char *argv[]) clutter_stage_add (stage, group); clutter_actor_show (group); - label = clutter_label_new_with_text ("Mono 26", "Clutter"); + label = clutter_text_new_with_text ("Mono 26", "Clutter"); clutter_actor_set_position (label, 120, 200); clutter_actor_show (label); diff --git a/tests/interactive/test-easing.c b/tests/interactive/test-easing.c index 555aa3c3a..22048ad4e 100644 --- a/tests/interactive/test-easing.c +++ b/tests/interactive/test-easing.c @@ -41,7 +41,7 @@ on_button_press (ClutterActor *actor, current_mode + 1, n_easing_modes); - clutter_label_set_text (CLUTTER_LABEL (easing_mode_label), text); + clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text); g_free (text); clutter_actor_get_size (main_stage, &stage_width, &stage_height); @@ -97,10 +97,10 @@ test_easing_main (int argc, char *argv[]) current_mode + 1, n_easing_modes); - label = clutter_label_new (); + label = clutter_text_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); - clutter_label_set_font_name (CLUTTER_LABEL (label), "Sans 18px"); - clutter_label_set_text (CLUTTER_LABEL (label), text); + clutter_text_set_font_name (CLUTTER_TEXT (label), "Sans 18px"); + clutter_text_set_text (CLUTTER_TEXT (label), text); clutter_actor_get_size (label, &label_width, &label_height); clutter_actor_set_position (label, stage_width - label_width - 10, diff --git a/tests/interactive/test-fbo.c b/tests/interactive/test-fbo.c index bdf99f1cb..f30cbf049 100644 --- a/tests/interactive/test-fbo.c +++ b/tests/interactive/test-fbo.c @@ -43,9 +43,9 @@ make_source(void) clutter_group_add (source, actor); - actor = clutter_label_new_with_text ("Sans Bold 50px", "Clutter"); + actor = clutter_text_new_with_text ("Sans Bold 50px", "Clutter"); - clutter_label_set_color (CLUTTER_LABEL (actor), &yellow); + clutter_text_set_color (CLUTTER_TEXT (actor), &yellow); clutter_actor_set_y (actor, clutter_actor_get_height(source) + 5); clutter_group_add (source, actor); diff --git a/tests/interactive/test-layout.c b/tests/interactive/test-layout.c index 9fe17222e..ea27f8e11 100644 --- a/tests/interactive/test-layout.c +++ b/tests/interactive/test-layout.c @@ -787,7 +787,7 @@ test_layout_main (int argc, char *argv[]) clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); - instructions = clutter_label_new_with_text ("Sans 14", + instructions = clutter_text_new_with_text ("Sans 14", "Instructions:\n" "a - add a new item\n" "d - remove last item\n" @@ -799,7 +799,7 @@ test_layout_main (int argc, char *argv[]) "s - use transformed box\n" "q - quit"); - clutter_label_set_use_markup (CLUTTER_LABEL (instructions), TRUE); + clutter_text_set_use_markup (CLUTTER_TEXT (instructions), TRUE); clutter_actor_set_position (instructions, 450, 10); clutter_container_add_actor (CLUTTER_CONTAINER (stage), instructions); diff --git a/tests/interactive/test-multistage.c b/tests/interactive/test-multistage.c index 19b9c750f..1021f8b90 100644 --- a/tests/interactive/test-multistage.c +++ b/tests/interactive/test-multistage.c @@ -50,10 +50,10 @@ on_button_press (ClutterActor *actor, clutter_container_add_actor (CLUTTER_CONTAINER (new_stage), tex); stage_label = g_strdup_printf ("Stage: %d", ++n_stages); - label = clutter_label_new_with_text ("Mono 12", stage_label); + label = clutter_text_new_with_text ("Mono 12", stage_label); - clutter_label_set_color (CLUTTER_LABEL (label), &white); - clutter_label_set_use_markup (CLUTTER_LABEL (label), TRUE); + clutter_text_set_color (CLUTTER_TEXT (label), &white); + clutter_text_set_use_markup (CLUTTER_TEXT (label), TRUE); width = (clutter_actor_get_width (new_stage) - clutter_actor_get_width (label)) / 2; height = (clutter_actor_get_height (new_stage) @@ -110,7 +110,7 @@ test_multistage_main (int argc, char *argv[]) G_CALLBACK (on_button_press), NULL); - label = clutter_label_new_with_text ("Mono 16", "Default stage"); + label = clutter_text_new_with_text ("Mono 16", "Default stage"); width = (clutter_actor_get_width (stage_default) - clutter_actor_get_width (label)) / 2; diff --git a/tests/interactive/test-project.c b/tests/interactive/test-project.c index 1ac2176b7..33560df0b 100644 --- a/tests/interactive/test-project.c +++ b/tests/interactive/test-project.c @@ -224,8 +224,8 @@ test_project_main (int argc, char *argv[]) clutter_actor_set_rotation (rect, CLUTTER_Y_AXIS, 60, 0, 0, 0); clutter_group_add (CLUTTER_GROUP (main_stage), rect); - label = clutter_label_new_with_text ("Mono 8pt", "Drag the blue rectangles"); - clutter_label_set_color (CLUTTER_LABEL (label), &white); + label = clutter_text_new_with_text ("Mono 8pt", "Drag the blue rectangles"); + clutter_text_set_color (CLUTTER_TEXT (label), &white); clutter_actor_set_position (label, 10, 10); clutter_group_add (CLUTTER_GROUP (main_stage), label); diff --git a/tests/interactive/test-random-text.c b/tests/interactive/test-random-text.c index 18ebefff0..b73ec4b4f 100644 --- a/tests/interactive/test-random-text.c +++ b/tests/interactive/test-random-text.c @@ -45,7 +45,7 @@ on_idle (gpointer data) font_names[rand () % FONT_NAME_COUNT], rand () % (MAX_FONT_SIZE - MIN_FONT_SIZE) + MIN_FONT_SIZE); - label = clutter_label_new_with_text (font_name, text); + label = clutter_text_new_with_text (font_name, text); if (clutter_actor_get_height (label) > line_height) line_height = clutter_actor_get_height (label); diff --git a/tests/interactive/test-rotate.c b/tests/interactive/test-rotate.c index 377a309db..b147a7d24 100644 --- a/tests/interactive/test-rotate.c +++ b/tests/interactive/test-rotate.c @@ -33,8 +33,8 @@ test_rotate_main (int argc, char *argv[]) clutter_actor_show (hand); clutter_container_add_actor (CLUTTER_CONTAINER (stage), hand); - label = clutter_label_new_with_text ("Mono 16", "The Wonder of the Spinning Hand"); - clutter_label_set_alignment (CLUTTER_LABEL (label), PANGO_ALIGN_CENTER); + label = clutter_text_new_with_text ("Mono 16", "The Wonder of the Spinning Hand"); + clutter_text_set_alignment (CLUTTER_TEXT (label), PANGO_ALIGN_CENTER); clutter_actor_set_position (label, 150, 150); clutter_actor_set_size (label, 500, 100); clutter_actor_show (label); diff --git a/tests/interactive/test-shader.c b/tests/interactive/test-shader.c index 4880a704a..1101d8246 100644 --- a/tests/interactive/test-shader.c +++ b/tests/interactive/test-shader.c @@ -354,7 +354,7 @@ test_shader_main (gint argc, gchar *argv[]) if (!child2) g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); child3 = clutter_rectangle_new (); - child4 = clutter_label_new_with_text ("Sans 20px", "Shady stuff"); + child4 = clutter_text_new_with_text ("Sans 20px", "Shady stuff"); clutter_rectangle_set_color (child3, &color); clutter_actor_set_size (child3, 50, 50); diff --git a/tests/interactive/test-stage-read-pixels.c b/tests/interactive/test-stage-read-pixels.c index f13b7ebe7..b0d813970 100644 --- a/tests/interactive/test-stage-read-pixels.c +++ b/tests/interactive/test-stage-read-pixels.c @@ -23,14 +23,14 @@ make_label (void) gchar *text; gchar *argv[] = { "ls", "--help", NULL }; - label = clutter_label_new (); - clutter_label_set_font_name (CLUTTER_LABEL (label), "Sans 10"); + label = clutter_text_new (); + clutter_text_set_font_name (CLUTTER_TEXT (label), "Sans 10"); if (g_spawn_sync (NULL, argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH, NULL, NULL, &text, NULL, NULL, NULL)) { - clutter_label_set_text (CLUTTER_LABEL (label), text); + clutter_text_set_text (CLUTTER_TEXT (label), text); g_free (text); } diff --git a/tests/interactive/test-unproject.c b/tests/interactive/test-unproject.c index 422647e84..ac76e6768 100644 --- a/tests/interactive/test-unproject.c +++ b/tests/interactive/test-unproject.c @@ -29,11 +29,10 @@ on_event (ClutterStage *stage, actor = clutter_stage_get_actor_at_pos (stage, x, y); - if (clutter_actor_transform_stage_point (actor, - CLUTTER_UNITS_FROM_DEVICE (x), - CLUTTER_UNITS_FROM_DEVICE (y), - &xu2, &yu2)) + CLUTTER_UNITS_FROM_DEVICE (x), + CLUTTER_UNITS_FROM_DEVICE (y), + &xu2, &yu2)) { gchar *txt; @@ -52,12 +51,11 @@ on_event (ClutterStage *stage, CLUTTER_UNITS_TO_DEVICE (xu2), CLUTTER_UNITS_TO_DEVICE (yu2)); - clutter_label_set_text (CLUTTER_LABEL (label), txt); + clutter_text_set_text (CLUTTER_TEXT (label), txt); g_free (txt); } else - clutter_label_set_text (CLUTTER_LABEL (label), - "Unprojection failed."); + clutter_text_set_text (CLUTTER_TEXT (label), "Unprojection failed."); } break; @@ -75,9 +73,9 @@ test_unproject_main (int argc, char *argv[]) gchar *txt; ClutterActor *rect, *stage, *label0; int i, rotate_x = 0, rotate_y = 60, rotate_z = 0; - ClutterColor stage_clr = { 0x0, 0x0, 0x0, 0xff }, + ClutterColor stage_clr = { 0x0, 0x0, 0x0, 0xff }, white = { 0xff, 0xff, 0xff, 0xff }, - blue = { 0, 0xff, 0xff, 0xff }; + blue = { 0x0, 0xff, 0xff, 0xff }; for (i = 0; i < argc; ++i) { @@ -95,11 +93,12 @@ test_unproject_main (int argc, char *argv[]) } else if (!strncmp (argv[i], "--help", 6)) { - printf ("%s [--rotage-x=degrees] [--rotage-y=degrees] " - "[--rotage-z=degrees]\n", - argv[0]); + g_print ("%s [--rotage-x=degrees] " + "[--rotage-y=degrees] " + "[--rotage-z=degrees]\n", + argv[0]); - exit (0); + return EXIT_FAILURE; } } @@ -124,8 +123,8 @@ test_unproject_main (int argc, char *argv[]) RECT_T, RECT_T + RECT_H, rotate_x, rotate_y, rotate_z); - label0 = clutter_label_new_with_text ("Mono 8pt", txt); - clutter_label_set_color (CLUTTER_LABEL (label0), &white); + label0 = clutter_text_new_with_text ("Mono 8pt", txt); + clutter_text_set_color (CLUTTER_TEXT (label0), &white); clutter_actor_set_position (label0, 10, 10); clutter_group_add (CLUTTER_GROUP (stage), label0); @@ -133,9 +132,9 @@ test_unproject_main (int argc, char *argv[]) g_free (txt); label = - clutter_label_new_with_text ("Mono 8pt", "Click around!"); + clutter_text_new_with_text ("Mono 8pt", "Click around!"); - clutter_label_set_color (CLUTTER_LABEL (label), &blue); + clutter_text_set_color (CLUTTER_TEXT (label), &blue); clutter_actor_set_position (label, 10, 50); clutter_group_add (CLUTTER_GROUP (stage), label); From ffa37dec2067ae69d2850a611758ea4d4ea48d0b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:48:45 +0000 Subject: [PATCH 045/108] Update the micro-bench tests to ClutterText Test the ClutterText rendering speed instead of ClutterLabel's. --- tests/micro-bench/test-text.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/micro-bench/test-text.c b/tests/micro-bench/test-text.c index a44f39d4e..afbb8b47b 100644 --- a/tests/micro-bench/test-text.c +++ b/tests/micro-bench/test-text.c @@ -80,14 +80,14 @@ main (int argc, char *argv[]) scale = 1.0; } - label = clutter_label_new_with_text (font_name, text); - clutter_label_set_color (CLUTTER_LABEL (label), &label_color); + label = clutter_text_new_with_text (font_name, text); + clutter_text_set_color (CLUTTER_TEXT (label), &label_color); clutter_actor_set_position (label, (1.0*STAGE_WIDTH/COLS)*col, (1.0*STAGE_HEIGHT/ROWS)*row); /*clutter_actor_set_clip (label, 0,0, (1.0*STAGE_WIDTH/COLS), (1.0*STAGE_HEIGHT/ROWS));*/ clutter_actor_set_scale (label, scale, scale); - clutter_label_set_line_wrap (CLUTTER_LABEL (label), FALSE); + clutter_text_set_line_wrap (CLUTTER_TEXT (label), FALSE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); } } From 5a1837bc9ef117d47cc9e6dbea66245c95027a4f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 13:49:39 +0000 Subject: [PATCH 046/108] Remove ClutterEntry and ClutterLabel from the build For the time being, just don't include them or compile them; the files will be removed from the repository as soon as all the documentation annotations have been ported over to ClutterText. --- clutter/Makefile.am | 4 ---- clutter/clutter.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/clutter/Makefile.am b/clutter/Makefile.am index f51523200..1ef03bd9a 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -63,7 +63,6 @@ source_h = \ $(srcdir)/clutter-container.h \ $(srcdir)/clutter-deprecated.h \ $(srcdir)/clutter-effect.h \ - $(srcdir)/clutter-entry.h \ $(srcdir)/clutter-event.h \ $(srcdir)/clutter-feature.h \ $(srcdir)/clutter-fixed.h \ @@ -71,7 +70,6 @@ source_h = \ $(srcdir)/clutter-group.h \ $(srcdir)/clutter-interval.h \ $(srcdir)/clutter-keysyms.h \ - $(srcdir)/clutter-label.h \ $(srcdir)/clutter-list-model.h \ $(srcdir)/clutter-main.h \ $(srcdir)/clutter-media.h \ @@ -154,7 +152,6 @@ source_c = \ clutter-color.c \ clutter-container.c \ clutter-effect.c \ - clutter-entry.c \ clutter-enum-types.c \ clutter-event.c \ clutter-feature.c \ @@ -163,7 +160,6 @@ source_c = \ clutter-group.c \ clutter-id-pool.c \ clutter-interval.c \ - clutter-label.c \ clutter-list-model.c \ clutter-main.c \ clutter-marshal.c \ diff --git a/clutter/clutter.h b/clutter/clutter.h index 664c88970..212870c41 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -46,14 +46,12 @@ #include "clutter-container.h" #include "clutter-deprecated.h" #include "clutter-effect.h" -#include "clutter-entry.h" #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-frame-source.h" #include "clutter-group.h" #include "clutter-interval.h" #include "clutter-keysyms.h" -#include "clutter-label.h" #include "clutter-list-model.h" #include "clutter-main.h" #include "clutter-media.h" From 0ac15903781b8deb9339027bdd4db2982f2629f3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 14:57:46 +0000 Subject: [PATCH 047/108] Lazily create the PangoContext on Text::init If we create the PangoContext for ClutterText inside the class initialization we might not have a Clutter main context yet. Ideally, we should store the Pango context inside the main context and create it on clutter_init(), but for now we can lazily create the PangoContext when we initialize a ClutterText instance for the first time. --- clutter/clutter-text.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4b657d1da..d746279aa 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1417,8 +1417,6 @@ clutter_text_class_init (ClutterTextClass *klass) ClutterBindingPool *binding_pool; GParamSpec *pspec; - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - g_type_class_add_private (klass, sizeof (ClutterTextPrivate)); gobject_class->set_property = clutter_text_set_property; @@ -1829,6 +1827,9 @@ clutter_text_init (ClutterText *self) ClutterTextPrivate *priv; int i; + if (G_UNLIKELY (_context == NULL)) + _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); + self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); priv->alignment = PANGO_ALIGN_LEFT; From a50dfefbc33a8c0b0c7189058f4f7f521707a8fc Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 15:00:22 +0000 Subject: [PATCH 048/108] Fix documentation issues Provide a description for the ClutterText section, and fix some misnamed arguments inside the header file. --- clutter/clutter-text.c | 16 ++++++++++++++++ clutter/clutter-text.h | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index d746279aa..c0d51543e 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -22,6 +22,22 @@ * License along with this library. If not, see . */ +/** + * SECTION:clutter-text + * @short_description: An actor for displaying and editing text + * + * #ClutterText is an actor that displays custom text using Pango + * as the text rendering engine. + * + * #ClutterText also allows inline editing of the text if the + * actor is set editable using clutter_text_set_editable(). + * + * Selection using keyboard or pointers can be enabled using + * clutter_text_set_selectable(). + * + * #ClutterText is available since Clutter 1.0 + */ + /* TODO: undo/redo hooks? */ diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 87f103486..56595b794 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -101,7 +101,7 @@ void clutter_text_set_ellipsize (ClutterText *self PangoEllipsizeMode mode); PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); void clutter_text_set_line_wrap (ClutterText *self, - gboolean wrap); + gboolean line_wrap); gboolean clutter_text_get_line_wrap (ClutterText *self); void clutter_text_set_line_wrap_mode (ClutterText *self, PangoWrapMode wrap_mode); @@ -123,7 +123,7 @@ gboolean clutter_text_get_justify (ClutterText *self void clutter_text_insert_unichar (ClutterText *self, gunichar wc); void clutter_text_delete_chars (ClutterText *self, - guint len); + guint n_chars); void clutter_text_insert_text (ClutterText *self, const gchar *text, gssize position); From c8dbbfaacff5b3d6ed2f9b28b65a10e15241dc0f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 11 Dec 2008 15:01:28 +0000 Subject: [PATCH 049/108] [docs] Update the API reference Add all the unused API to the sections file; there are still some undocumented bits, but clutter-unused.txt is empty for the time being. Also, add ClutterText to the main XML document and remove ClutterLabel and ClutterEntry. --- doc/reference/clutter/Makefile.am | 1 + doc/reference/clutter/clutter-docs.xml | 3 +- doc/reference/clutter/clutter-sections.txt | 131 ++++++++++++++++++++- doc/reference/clutter/clutter.types | 3 +- 4 files changed, 132 insertions(+), 6 deletions(-) diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am index ca941565f..d03a283d0 100644 --- a/doc/reference/clutter/Makefile.am +++ b/doc/reference/clutter/Makefile.am @@ -55,6 +55,7 @@ CFILE_GLOB=$(top_srcdir)/clutter/*.c # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES=\ clutter.h \ + clutter-bezier.h \ clutter-debug.h \ clutter-deprecated.h \ clutter-enum-types.h \ diff --git a/doc/reference/clutter/clutter-docs.xml b/doc/reference/clutter/clutter-docs.xml index 90b7d0d8a..8807ed3a8 100644 --- a/doc/reference/clutter/clutter-docs.xml +++ b/doc/reference/clutter/clutter-docs.xml @@ -57,8 +57,7 @@ - - + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index dd4061fc2..9fb7924dd 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -170,6 +170,9 @@ clutter_smoothstep_inc_func clutter_smoothstep_dec_func clutter_exp_inc_func clutter_exp_dec_func +clutter_exp_in_func +clutter_exp_out_func +clutter_exp_in_out_func clutter_ease_in_func clutter_ease_out_func clutter_ease_in_out_func @@ -438,6 +441,8 @@ clutter_actor_get_reactive clutter_actor_set_shader clutter_actor_get_shader clutter_actor_set_shader_param +clutter_actor_set_shader_param_int +clutter_actor_set_shader_param_float clutter_actor_set_depthu @@ -471,6 +476,9 @@ clutter_actor_get_scalex clutter_actor_set_rotationx clutter_actor_get_rotationx + +clutter_actor_grab_key_focus + CLUTTER_TYPE_GEOMETRY CLUTTER_TYPE_ACTOR_BOX @@ -693,6 +701,8 @@ ClutterPathCallback ClutterPathNodeType clutter_path_new clutter_path_new_with_description + + clutter_path_add_move_to clutter_path_add_rel_move_to clutter_path_add_line_to @@ -702,6 +712,8 @@ clutter_path_add_rel_curve_to clutter_path_add_close clutter_path_add_string clutter_path_add_node + + clutter_path_get_n_nodes clutter_path_get_node clutter_path_get_nodes @@ -720,6 +732,21 @@ ClutterPathNode clutter_path_node_copy clutter_path_node_free clutter_path_node_equal + + +CLUTTER_TYPE_PATH +CLUTTER_TYPE_PATH_NODE +CLUTTER_PATH +CLUTTER_PATH_CLASS +CLUTTER_IS_PATH +CLUTTER_IS_PATH_CLASS +CLUTTER_PATH_GET_CLASS +CLUTTER_PATH_RELATIVE + + +ClutterPathPrivate +clutter_path_get_type +clutter_path_node_get_type
@@ -1510,11 +1537,27 @@ clutter_shader_release clutter_shader_is_compiled clutter_shader_set_is_enabled clutter_shader_get_is_enabled -clutter_shader_set_uniform_1f + + clutter_shader_set_uniform clutter_shader_get_cogl_program clutter_shader_get_cogl_fragment_shader clutter_shader_get_cogl_vertex_shader +clutter_shader_set_uniform_1f + + +ClutterShaderFloat +CLUTTER_VALUE_HOLDS_SHADER_FLOAT +clutter_value_set_shader_float +clutter_value_get_shader_float +ClutterShaderInt +CLUTTER_VALUE_HOLDS_SHADER_INT +clutter_value_set_shader_int +clutter_value_get_shader_int +ClutterShaderMatrix +CLUTTER_VALUE_HOLDS_SHADER_MATRIX +clutter_value_set_shader_matrix +clutter_value_get_shader_matrix CLUTTER_IS_SHADER @@ -1524,10 +1567,17 @@ CLUTTER_SHADER_CLASS CLUTTER_SHADER_ERROR CLUTTER_SHADER_GET_CLASS CLUTTER_TYPE_SHADER +CLUTTER_TYPE_SHADER_FLOAT +CLUTTER_TYPE_SHADER_INT +CLUTTER_TYPE_SHADER_MATRIX + ClutterShaderPrivate clutter_shader_get_type clutter_shader_error_quark +clutter_shader_float_get_type +clutter_shader_int_get_type +clutter_shader_matrix_get_type
@@ -1593,6 +1643,10 @@ clutter_interval_peek_final_value clutter_interval_set_interval clutter_interval_get_interval + +clutter_interval_compute_value +clutter_interval_validate + CLUTTER_TYPE_INTERVAL CLUTTER_INTERVAL @@ -1614,7 +1668,7 @@ ClutterBindingActionFunc clutter_binding_pool_new -clutter_binding_pool_get_from_class +clutter_binding_pool_get_for_class clutter_binding_pool_find @@ -1629,3 +1683,76 @@ clutter_binding_pool_unblock_action clutter_binding_pool_activate
+ +
+ClutterText +clutter-text +ClutterText +ClutterTextClass +clutter_text_new +clutter_text_new_full +clutter_text_new_with_text + + +clutter_text_set_text +clutter_text_get_text +clutter_text_set_activatable +clutter_text_get_activatable +clutter_text_set_alignment +clutter_text_get_alignment +clutter_text_set_attributes +clutter_text_get_attributes +clutter_text_set_color +clutter_text_get_color +clutter_text_set_ellipsize +clutter_text_get_ellipsize +clutter_text_set_font_name +clutter_text_get_font_name +clutter_text_set_invisible_char +clutter_text_get_invisible_char +clutter_text_set_justify +clutter_text_get_justify +clutter_text_get_layout +clutter_text_set_line_wrap +clutter_text_get_line_wrap +clutter_text_set_line_wrap_mode +clutter_text_get_line_wrap_mode +clutter_text_set_max_length +clutter_text_get_max_length +clutter_text_set_selectable +clutter_text_get_selectable +clutter_text_get_selection +clutter_text_set_selection_bound +clutter_text_get_selection_bound +clutter_text_set_text_visible +clutter_text_get_text_visible +clutter_text_set_use_markup +clutter_text_get_use_markup + + +clutter_text_set_editable +clutter_text_get_editable +clutter_text_insert_text +clutter_text_insert_unichar +clutter_text_delete_chars +clutter_text_delete_text +clutter_text_get_chars +clutter_text_set_cursor_color +clutter_text_get_cursor_color +clutter_text_set_cursor_position +clutter_text_get_cursor_position +clutter_text_set_cursor_visible +clutter_text_get_cursor_visible + + +CLUTTER_IS_TEXT +CLUTTER_IS_TEXT_CLASS +CLUTTER_TEXT +CLUTTER_TEXT_CLASS +CLUTTER_TEXT_GET_CLASS +CLUTTER_TYPE_TEXT + + +ClutterTextPrivate +clutter_text_get_type +
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 2e660766c..4a30ab962 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -6,7 +6,6 @@ clutter_stage_get_type clutter_rectangle_get_type clutter_texture_get_type clutter_clone_texture_get_type -clutter_label_get_type clutter_timeline_get_type clutter_media_get_type clutter_behaviour_get_type @@ -19,7 +18,6 @@ clutter_path_get_type clutter_behaviour_rotate_get_type clutter_behaviour_scale_get_type clutter_backend_get_type -clutter_entry_get_type clutter_script_get_type clutter_scriptable_get_type clutter_model_get_type @@ -28,3 +26,4 @@ clutter_list_model_get_type clutter_score_get_type clutter_shader_get_type clutter_child_meta_get_type +clutter_text_get_type From 39b575e6b43301354912d18a50b80085e8d92388 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 15:08:12 +0000 Subject: [PATCH 050/108] Remove ClutterLabel and ClutterEntry Both ClutterLabel and ClutterEntry have been deprecated by ClutterText. --- clutter/clutter-entry.c | 1795 --------------------------------------- clutter/clutter-entry.h | 165 ---- clutter/clutter-label.c | 1347 ----------------------------- clutter/clutter-label.h | 128 --- 4 files changed, 3435 deletions(-) delete mode 100644 clutter/clutter-entry.c delete mode 100644 clutter/clutter-entry.h delete mode 100644 clutter/clutter-label.c delete mode 100644 clutter/clutter-label.h diff --git a/clutter/clutter-entry.c b/clutter/clutter-entry.c deleted file mode 100644 index 1ae661eda..000000000 --- a/clutter/clutter-entry.c +++ /dev/null @@ -1,1795 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * Neil Jagdish Patel - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:clutter-entry - * @short_description: A single line text entry actor - * - * #ClutterEntry is a #ClutterTexture that allows single line text entry. - * - * #ClutterEntry is available since Clutter 0.4. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "clutter-entry.h" - -#include "clutter-debug.h" -#include "clutter-enum-types.h" -#include "clutter-keysyms.h" -#include "clutter-main.h" -#include "clutter-marshal.h" -#include "clutter-private.h" -#include "clutter-rectangle.h" -#include "clutter-units.h" - -#include "cogl-pango.h" - -#define DEFAULT_FONT_NAME "Sans 10" -#define ENTRY_CURSOR_WIDTH 1 -#define ENTRY_PADDING 5 - -G_DEFINE_TYPE (ClutterEntry, clutter_entry, CLUTTER_TYPE_ACTOR); - -/* Probably move into main */ -static PangoContext *_context = NULL; - -static const ClutterColor default_text_color = { 0, 0, 0, 255 }; - -enum -{ - PROP_0, - - PROP_FONT_NAME, - PROP_TEXT, - PROP_COLOR, - PROP_ALIGNMENT, /* FIXME */ - PROP_POSITION, - PROP_CURSOR, - PROP_TEXT_VISIBLE, - PROP_MAX_LENGTH, - PROP_ENTRY_PADDING, - PROP_X_ALIGN -}; - -enum -{ - TEXT_CHANGED, - CURSOR_EVENT, - ACTIVATE, - - LAST_SIGNAL -}; - -static guint entry_signals[LAST_SIGNAL] = { 0, }; - -#define CLUTTER_ENTRY_GET_PRIVATE(obj) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ENTRY, ClutterEntryPrivate)) - -struct _ClutterEntryPrivate -{ - PangoContext *context; - PangoFontDescription *desc; - - ClutterColor fgcol; - - gchar *text; - gchar *font_name; - gboolean text_visible; - gunichar priv_char; - - gint extents_width; - gint extents_height; - - gint width; - gint n_chars; /* number of chars */ - gint n_bytes; /* number of bytes */ - - guint alignment : 2; - guint wrap : 1; - guint use_underline : 1; - guint use_markup : 1; - guint ellipsize : 3; - guint single_line_mode : 1; - guint wrap_mode : 3; - gint position; - gint text_x; - gint max_length; - gint entry_padding; - gdouble x_align; - - PangoAttrList *attrs; - PangoAttrList *effective_attrs; - PangoLayout *layout; - gint width_chars; - - ClutterGeometry cursor_pos; - gboolean show_cursor; -}; - -static void -clutter_entry_set_entry_padding (ClutterEntry *entry, - guint padding) -{ - ClutterEntryPrivate *priv = entry->priv; - - if (priv->entry_padding != padding) - { - priv->entry_padding = padding; - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_object_notify (G_OBJECT (entry), "entry-padding"); - } -} - -static void -clutter_entry_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - - entry = CLUTTER_ENTRY (object); - priv = entry->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - clutter_entry_set_font_name (entry, g_value_get_string (value)); - break; - case PROP_TEXT: - clutter_entry_set_text (entry, g_value_get_string (value)); - break; - case PROP_COLOR: - clutter_entry_set_color (entry, clutter_value_get_color (value)); - break; - case PROP_ALIGNMENT: - clutter_entry_set_alignment (entry, g_value_get_enum (value)); - break; - case PROP_POSITION: - clutter_entry_set_cursor_position (entry, g_value_get_int (value)); - break; - case PROP_CURSOR: - clutter_entry_set_visible_cursor (entry, g_value_get_boolean (value)); - break; - case PROP_TEXT_VISIBLE: - clutter_entry_set_visibility (entry, g_value_get_boolean (value)); - break; - case PROP_MAX_LENGTH: - clutter_entry_set_max_length (entry, g_value_get_int (value)); - break; - case PROP_ENTRY_PADDING: - clutter_entry_set_entry_padding (entry, g_value_get_uint (value)); - break; - case PROP_X_ALIGN: - entry->priv->x_align = g_value_get_double (value); - clutter_actor_queue_redraw (CLUTTER_ACTOR (object)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_entry_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - - entry = CLUTTER_ENTRY(object); - priv = entry->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - g_value_set_string (value, priv->font_name); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_COLOR: - clutter_value_set_color (value, &priv->fgcol); - break; - case PROP_ALIGNMENT: - g_value_set_enum (value, priv->alignment); - break; - case PROP_POSITION: - g_value_set_int (value, priv->position); - break; - case PROP_CURSOR: - g_value_set_boolean (value, priv->show_cursor); - break; - case PROP_TEXT_VISIBLE: - g_value_set_boolean (value, priv->text_visible); - break; - case PROP_MAX_LENGTH: - g_value_set_int (value, priv->max_length); - break; - case PROP_ENTRY_PADDING: - g_value_set_uint (value, priv->entry_padding); - break; - case PROP_X_ALIGN: - g_value_set_double (value, priv->x_align); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_entry_ensure_layout (ClutterEntry *entry, gint width) -{ - ClutterEntryPrivate *priv; - - priv = entry->priv; - - if (!priv->layout) - { - priv->layout = pango_layout_new (_context); - - if (priv->effective_attrs) - pango_layout_set_attributes (priv->layout, priv->effective_attrs); - - pango_layout_set_alignment (priv->layout, priv->alignment); - pango_layout_set_ellipsize (priv->layout, priv->ellipsize); - pango_layout_set_single_paragraph_mode (priv->layout, - priv->single_line_mode); - - pango_layout_set_font_description (priv->layout, priv->desc); - - if (priv->text_visible) - pango_layout_set_text (priv->layout, priv->text, priv->n_bytes); - else - { - GString *str = g_string_sized_new (priv->n_bytes); - gunichar invisible_char; - gchar buf[7]; - gint char_len, i; - - if (priv->priv_char != 0) - invisible_char = priv->priv_char; - else - invisible_char = '*'; - - /* we need to convert the string built of invisible characters - * into UTF-8 for it to be fed to the Pango layout - */ - memset (buf, 0, sizeof (buf)); - char_len = g_unichar_to_utf8 (invisible_char, buf); - - for (i = 0; i < priv->n_chars; i++) - g_string_append_len (str, buf, char_len); - - pango_layout_set_text (priv->layout, str->str, str->len); - - g_string_free (str, TRUE); - } - - if (priv->wrap) - pango_layout_set_wrap (priv->layout, priv->wrap_mode); - - if (priv->wrap && width > 0) - pango_layout_set_width (priv->layout, width * PANGO_SCALE); - else - pango_layout_set_width (priv->layout, -1); - - /* Prime the cache for the layout */ - cogl_pango_ensure_glyph_cache_for_layout (priv->layout); - } -} - -static void -clutter_entry_clear_layout (ClutterEntry *entry) -{ - if (entry->priv->layout) - { - g_object_unref (entry->priv->layout); - entry->priv->layout = NULL; - } -} - -static gint -offset_to_bytes (const gchar *text, gint pos) -{ - gchar *c = NULL; - gint i, j, len; - - if (pos < 1) - return pos; - - c = g_utf8_next_char (text); - j = 1; - len = strlen (text); - - for (i = 0; i < len; i++) - { - if (&text[i] == c) - { - if (j == pos) - break; - else - { - c = g_utf8_next_char (c); - j++; - } - } - } - return i; -} - - -static void -clutter_entry_ensure_cursor_position (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - gint index_; - PangoRectangle rect; - gint priv_char_bytes; - - priv = entry->priv; - - /* If characters are invisible, get the byte-length of the invisible - * character. If priv_char is 0, we use '*', which is ASCII (1 byte). - */ - if (!priv->text_visible && priv->priv_char) - priv_char_bytes = g_unichar_to_utf8 (priv->priv_char, NULL); - else - priv_char_bytes = 1; - - if (priv->position == -1) - { - if (priv->text_visible) - index_ = strlen (priv->text); - else - index_ = priv->n_chars * priv_char_bytes; - } - else - { - if (priv->text_visible) - index_ = offset_to_bytes (priv->text, priv->position); - else - index_ = priv->position * priv_char_bytes; - } - - pango_layout_get_cursor_pos (priv->layout, index_, &rect, NULL); - priv->cursor_pos.x = rect.x / PANGO_SCALE; - priv->cursor_pos.y = rect.y / PANGO_SCALE; - priv->cursor_pos.width = ENTRY_CURSOR_WIDTH; - priv->cursor_pos.height = rect.height / PANGO_SCALE; - - g_signal_emit (entry, entry_signals[CURSOR_EVENT], 0, &priv->cursor_pos); -} - -static void -clutter_entry_clear_cursor_position (ClutterEntry *entry) -{ - entry->priv->cursor_pos.width = 0; -} - -void -clutter_entry_paint_cursor (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - priv = entry->priv; - - if (priv->show_cursor) - { - cogl_set_source_color4ub (priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - priv->fgcol.alpha); - - cogl_rectangle (priv->cursor_pos.x, - priv->cursor_pos.y, - priv->cursor_pos.width, - priv->cursor_pos.height); - } -} - -static void -clutter_entry_paint (ClutterActor *self) -{ - ClutterEntry *entry; - ClutterEntryPrivate *priv; - PangoRectangle logical; - gint width, actor_width; - gint text_width; - gint cursor_x; - CoglColor color = { 0, }; - - entry = CLUTTER_ENTRY(self); - priv = entry->priv; - - if (priv->desc == NULL || priv->text == NULL) - { - CLUTTER_NOTE (ACTOR, "layout: %p , desc: %p, text %p", - priv->layout, - priv->desc, - priv->text); - return; - } - - if (priv->width < 0) - width = clutter_actor_get_width (self); - else - width = priv->width; - - cogl_clip_set (0, 0, - COGL_FIXED_FROM_INT (width), - COGL_FIXED_FROM_INT (clutter_actor_get_height (self))); - - actor_width = width - (2 * priv->entry_padding); - clutter_entry_ensure_layout (entry, actor_width); - clutter_entry_ensure_cursor_position (entry); - - pango_layout_get_extents (priv->layout, NULL, &logical); - text_width = logical.width / PANGO_SCALE; - - if (actor_width < text_width) - { - /* We need to do some scrolling */ - cursor_x = priv->cursor_pos.x; - - /* If the cursor is at the begining or the end of the text, the placement - * is easy, however, if the cursor is in the middle somewhere, we need to - * make sure the text doesn't move until the cursor is either in the - * far left or far right - */ - - if (priv->position == 0) - priv->text_x = 0; - else if (priv->position == -1) - { - priv->text_x = actor_width - text_width; - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - else - { - if (priv->text_x <= 0) - { - gint diff = -1 * priv->text_x; - - if (cursor_x < diff) - priv->text_x += diff - cursor_x; - else if (cursor_x > (diff + actor_width)) - priv->text_x -= cursor_x - (diff+actor_width); - } - - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - - } - else - { - priv->text_x = (actor_width - text_width) * priv->x_align; - priv->cursor_pos.x += priv->text_x + priv->entry_padding; - } - - cogl_color_set_from_4ub (&color, - priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - clutter_actor_get_paint_opacity (self)); - - cogl_pango_render_layout (priv->layout, - priv->text_x + priv->entry_padding, 0, - &color, 0); - - if (CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor) - CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor (entry); - - cogl_clip_unset (); -} - -static void -clutter_entry_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean absolute_origin_changed) -{ - ClutterEntry *entry = CLUTTER_ENTRY (self); - ClutterEntryPrivate *priv = entry->priv; - gint width; - - width = CLUTTER_UNITS_TO_DEVICE (box->x2 - box->x1); - - if (priv->width != width) - { - clutter_entry_clear_layout (entry); - clutter_entry_ensure_layout (entry, width); - - priv->width = width; - } - - CLUTTER_ACTOR_CLASS (clutter_entry_parent_class)->allocate (self, box, absolute_origin_changed); -} - -static inline void -clutter_entry_handle_key_event_internal (ClutterEntry *entry, - ClutterKeyEvent *event) -{ - gunichar key_unichar; - ClutterEntryPrivate *priv = entry->priv; - gint pos = priv->position; - gint len = 0; - gint keyval = clutter_key_event_symbol (event); - - if (priv->text) - len = g_utf8_strlen (priv->text, -1); - - switch (keyval) - { - case CLUTTER_Return: - case CLUTTER_KP_Enter: - case CLUTTER_ISO_Enter: - g_signal_emit (entry, entry_signals[ACTIVATE], 0); - break; - - case CLUTTER_Escape: - case CLUTTER_Up: - case CLUTTER_KP_Up: - case CLUTTER_Down: - case CLUTTER_KP_Down: - case CLUTTER_Shift_L: - case CLUTTER_Shift_R: - break; - - case CLUTTER_BackSpace: - if (pos != 0 && len != 0) - clutter_entry_delete_chars (entry, 1); - break; - - case CLUTTER_Delete: - case CLUTTER_KP_Delete: - if (len && pos != -1) - clutter_entry_delete_text (entry, pos, pos+1);; - break; - - case CLUTTER_Left: - case CLUTTER_KP_Left: - if (pos != 0 && len != 0) - { - if (pos == -1) - clutter_entry_set_cursor_position (entry, len - 1); - else - clutter_entry_set_cursor_position (entry, pos - 1); - } - break; - - case CLUTTER_Right: - case CLUTTER_KP_Right: - if (pos != -1 && len != 0) - { - if (pos != len) - clutter_entry_set_cursor_position (entry, pos + 1); - } - break; - - case CLUTTER_End: - case CLUTTER_KP_End: - clutter_entry_set_cursor_position (entry, -1); - break; - - case CLUTTER_Begin: - case CLUTTER_Home: - case CLUTTER_KP_Home: - clutter_entry_set_cursor_position (entry, 0); - break; - - default: - key_unichar = clutter_key_event_unicode (event); - if (g_unichar_validate (key_unichar)) - clutter_entry_insert_unichar (entry, key_unichar); - break; - } -} - -static gboolean -clutter_entry_key_press (ClutterActor *actor, - ClutterKeyEvent *event) -{ - clutter_entry_handle_key_event_internal (CLUTTER_ENTRY (actor), event); - - return TRUE; -} - -static void -clutter_entry_dispose (GObject *object) -{ - ClutterEntry *self = CLUTTER_ENTRY(object); - ClutterEntryPrivate *priv; - - priv = self->priv; - - if (priv->layout) - { - g_object_unref (priv->layout); - priv->layout = NULL; - } - - if (priv->context) - { - g_object_unref (priv->context); - priv->context = NULL; - } - - G_OBJECT_CLASS (clutter_entry_parent_class)->dispose (object); -} - -static void -clutter_entry_finalize (GObject *object) -{ - ClutterEntryPrivate *priv = CLUTTER_ENTRY (object)->priv; - - if (priv->desc) - pango_font_description_free (priv->desc); - - g_free (priv->text); - g_free (priv->font_name); - - G_OBJECT_CLASS (clutter_entry_parent_class)->finalize (object); -} - -static void -clutter_entry_class_init (ClutterEntryClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; - - klass->paint_cursor = clutter_entry_paint_cursor; - - actor_class->paint = clutter_entry_paint; - actor_class->allocate = clutter_entry_allocate; - actor_class->key_press_event = clutter_entry_key_press; - - gobject_class->finalize = clutter_entry_finalize; - gobject_class->dispose = clutter_entry_dispose; - gobject_class->set_property = clutter_entry_set_property; - gobject_class->get_property = clutter_entry_get_property; - - /** - * ClutterEntry:font-name: - * - * The font to be used by the entry, expressed in a string that - * can be parsed by pango_font_description_from_string(). - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_FONT_NAME, - g_param_spec_string ("font-name", - "Font Name", - "Pango font description", - NULL, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:text: - * - * The text inside the entry. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_TEXT, - g_param_spec_string ("text", - "Text", - "Text to render", - NULL, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:color: - * - * The color of the text inside the entry. - * - * Since: 0.4 - */ - pspec = clutter_param_spec_color ("color", - "Color", - "The color of the text", - &default_text_color, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_COLOR, pspec); - /** - * ClutterEntry:alignment: - * - * The preferred alignment for the string. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_ALIGNMENT, - g_param_spec_enum ("alignment", - "Alignment", - "The preferred alignment for the string,", - PANGO_TYPE_ALIGNMENT, - PANGO_ALIGN_LEFT, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:position: - * - * The current input cursor position. -1 is taken to be the end of the text - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_POSITION, - g_param_spec_int ("position", - "Position", - "The cursor position", - -1, G_MAXINT, - -1, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:cursor-visible: - * - * Whether the input cursor is visible or not. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_CURSOR, - g_param_spec_boolean ( "cursor-visible", - "Cursor Visible", - "Whether the input cursor is visible", - TRUE, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:text-visible: - * - * Whether the text is visible in plain form, or replaced by the - * character set by clutter_entry_set_invisible_char(). - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_TEXT_VISIBLE, - g_param_spec_boolean ("text-visible", - "Text Visible", - "Whether the text is visible in plain form", - TRUE, - CLUTTER_PARAM_READWRITE)); - - /** - * ClutterEntry:max-length: - * - * The maximum length of the entry text. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_MAX_LENGTH, - g_param_spec_int ("max-length", - "Max Length", - "The maximum length of the entry text", - 0, G_MAXINT, - 0, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:entry-padding: - * - * The padding space between the text and the entry right and left borders. - * - * Since: 0.4 - */ - g_object_class_install_property - (gobject_class, PROP_ENTRY_PADDING, - g_param_spec_uint ("entry-padding", - "Entry Padding", - "The padding space between the text and the left and " - "right borders", - 0, G_MAXUINT, - ENTRY_PADDING, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry:x-align: - * - * Horizontal alignment to be used for the text (0.0 for left alignment, - * 1.0 for right alignment). - * - * Since: 0.6 - */ - g_object_class_install_property (gobject_class, - PROP_X_ALIGN, - g_param_spec_double ("x-align", - "Horizontal Alignment", - "The horizontal alignment to be used for the text", - 0.0, 1.0, 0.0, - CLUTTER_PARAM_READWRITE)); - /** - * ClutterEntry::text-changed: - * @entry: the actor which received the event - * - * The ::text-changed signal is emitted after @entry's text changes - */ - entry_signals[TEXT_CHANGED] = - g_signal_new ("text-changed", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, text_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /** - * ClutterEntry::cursor-event: - * @entry: the actor which received the event - * @geometry: a #ClutterGeometry - * - * The ::cursor-event signal is emitted each time the input cursor's geometry - * changes, this could be a positional or size change. If you would like to - * implement your own input cursor, set the cursor-visible property to %FALSE, - * and connect to this signal to position and size your own cursor. - * - * Since: 0.4 - */ - entry_signals[CURSOR_EVENT] = - g_signal_new ("cursor-event", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), - NULL, NULL, - clutter_marshal_VOID__BOXED, - G_TYPE_NONE, 1, - CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); - - /** - * ClutterEntry::activate: - * @entry: the actor which received the event - * - * The ::activate signal is emitted each time the entry is 'activated' - * by the user, normally by pressing the 'Enter' key. - * - * Since: 0.4 - */ - entry_signals[ACTIVATE] = - g_signal_new ("activate", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterEntryClass, activate), - NULL, NULL, - clutter_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - g_type_class_add_private (gobject_class, sizeof (ClutterEntryPrivate)); -} - -static void -clutter_entry_init (ClutterEntry *self) -{ - ClutterEntryPrivate *priv; - gdouble resolution; - gint font_size; - - self->priv = priv = CLUTTER_ENTRY_GET_PRIVATE (self); - - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - - resolution = pango_cairo_context_get_resolution (_context); - - priv->alignment = PANGO_ALIGN_LEFT; - priv->wrap = FALSE; - priv->wrap_mode = PANGO_WRAP_WORD; - priv->ellipsize = PANGO_ELLIPSIZE_NONE; - priv->use_underline = FALSE; - priv->use_markup = FALSE; - priv->layout = NULL; - priv->text = NULL; - priv->attrs = NULL; - priv->position = -1; - priv->priv_char = '*'; - priv->text_visible = TRUE; - priv->text_x = 0; - priv->max_length = 0; - priv->entry_padding = ENTRY_PADDING; - priv->x_align = 0.0; - - priv->fgcol = default_text_color; - - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->desc = pango_font_description_from_string (priv->font_name); - - /* we use the font size to set the default width and height, in case - * the user doesn't call clutter_actor_set_size(). - */ - font_size = PANGO_PIXELS (pango_font_description_get_size (priv->desc)) - * resolution - / 72.0; - clutter_actor_set_size (CLUTTER_ACTOR (self), font_size * 20, 50); - - priv->show_cursor = TRUE; -} - -/** - * clutter_entry_new_with_text: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * - * Creates a new #ClutterEntry displaying @text using @font_name. - * - * Return value: the newly created #ClutterEntry - * - * Since: 0.4 - */ -ClutterActor * -clutter_entry_new_with_text (const gchar *font_name, - const gchar *text) -{ - ClutterActor *entry = clutter_entry_new (); - - g_object_set (entry, - "font-name", font_name, - "text", text, - NULL); - return entry; -} - -/** - * clutter_entry_new_full: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * @color: #ClutterColor for text - * - * Creates a new #ClutterEntry displaying @text with @color - * using @font_name. - * - * Return value: the newly created #ClutterEntry - * - * Since: 0.4 - */ -ClutterActor * -clutter_entry_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) -{ - ClutterActor *entry; - - entry = clutter_entry_new_with_text (font_name, text); - clutter_entry_set_color (CLUTTER_ENTRY(entry), color); - - return entry; -} - -/** - * clutter_entry_new: - * - * Creates a new, empty #ClutterEntry. - * - * Returns: the newly created #ClutterEntry - */ -ClutterActor * -clutter_entry_new (void) -{ - ClutterActor *entry = g_object_new (CLUTTER_TYPE_ENTRY, - NULL); - clutter_actor_set_size (entry, 50, 50); - - return entry; -} - -/** - * clutter_entry_get_text: - * @entry: a #ClutterEntry - * - * Retrieves the text displayed by @entry. - * - * Return value: the text of the entry. The returned string is - * owned by #ClutterEntry and should not be modified or freed. - * - * Since: 0.4 - */ -G_CONST_RETURN gchar * -clutter_entry_get_text (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - return entry->priv->text; -} - -/** - * clutter_entry_set_text: - * @entry: a #ClutterEntry - * @text: the text to be displayed - * - * Sets @text as the text to be displayed by @entry. The - * ClutterEntry::text-changed signal is emitted. - * - * Since: 0.4 - */ -void -clutter_entry_set_text (ClutterEntry *entry, - const gchar *text) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (text != NULL); - - priv = entry->priv; - - g_object_ref (entry); - - if (priv->max_length > 0) - { - gint len = g_utf8_strlen (text, -1); - - if (len < priv->max_length) - { - g_free (priv->text); - - priv->text = g_strdup (text); - priv->n_bytes = strlen (text); - priv->n_chars = len; - } - else - { - gchar * n = g_malloc0 (priv->max_length + 1); - - g_utf8_strncpy (n, text, priv->max_length); - g_free (priv->text); - - priv->text = n; - priv->n_bytes = strlen (n); - priv->n_chars = priv->max_length; - } - } - else - { - g_free (priv->text); - - priv->text = g_strdup (text); - priv->n_bytes = strlen (text); - priv->n_chars = g_utf8_strlen (priv->text, -1); - } - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - /* Recreate the layout so the glyph cache will be primed */ - clutter_entry_ensure_layout (entry, -1); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_signal_emit (G_OBJECT (entry), entry_signals[TEXT_CHANGED], 0); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_get_font_name: - * @entry: a #ClutterEntry - * - * Retrieves the font used by @entry. - * - * Return value: a string containing the font name, in a format - * understandable by pango_font_description_from_string(). The - * string is owned by #ClutterEntry and should not be modified - * or freed. - * - * Since: 0.4 - */ -G_CONST_RETURN gchar * -clutter_entry_get_font_name (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - return entry->priv->font_name; -} - -/** - * clutter_entry_set_font_name: - * @entry: a #ClutterEntry - * @font_name: a font name and size, or %NULL for the default font - * - * Sets @font_name as the font used by @entry. - * - * @font_name must be a string containing the font name and its - * size, similarly to what you would feed to the - * pango_font_description_from_string() function. - * - * Since: 0.4 - */ -void -clutter_entry_set_font_name (ClutterEntry *entry, - const gchar *font_name) -{ - ClutterEntryPrivate *priv; - PangoFontDescription *desc; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; - - priv = entry->priv; - - if (strcmp (priv->font_name, font_name) == 0) - return; - - desc = pango_font_description_from_string (font_name); - if (!desc) - { - g_warning ("Attempting to create a PangoFontDescription for " - "font name `%s', but failed.", - font_name); - return; - } - - g_object_ref (entry); - - g_free (priv->font_name); - priv->font_name = g_strdup (font_name); - - if (priv->desc) - pango_font_description_free (priv->desc); - - priv->desc = desc; - - if (entry->priv->text && entry->priv->text[0] != '\0') - { - clutter_entry_clear_layout (entry); - /* Recreate the layout so the glyph cache will be primed */ - clutter_entry_ensure_layout (entry, -1); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - } - - g_object_notify (G_OBJECT (entry), "font-name"); - g_object_unref (entry); -} - - -/** - * clutter_entry_set_color: - * @entry: a #ClutterEntry - * @color: a #ClutterColor - * - * Sets the color of @entry. - * - * Since: 0.4 - */ -void -clutter_entry_set_color (ClutterEntry *entry, - const ClutterColor *color) -{ - ClutterActor *actor; - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (color != NULL); - - priv = entry->priv; - - g_object_ref (entry); - - priv->fgcol.red = color->red; - priv->fgcol.green = color->green; - priv->fgcol.blue = color->blue; - priv->fgcol.alpha = color->alpha; - - actor = CLUTTER_ACTOR (entry); - - clutter_actor_set_opacity (actor, priv->fgcol.alpha); - - if (CLUTTER_ACTOR_IS_VISIBLE (actor)) - clutter_actor_queue_redraw (actor); - - g_object_notify (G_OBJECT (entry), "color"); - g_object_unref (entry); -} - -/** - * clutter_entry_get_color: - * @entry: a #ClutterEntry - * @color: return location for a #ClutterColor - * - * Retrieves the color of @entry. - * - * Since: 0.4 - */ -void -clutter_entry_get_color (ClutterEntry *entry, - ClutterColor *color) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (color != NULL); - - priv = entry->priv; - - color->red = priv->fgcol.red; - color->green = priv->fgcol.green; - color->blue = priv->fgcol.blue; - color->alpha = priv->fgcol.alpha; -} - -/** - * clutter_entry_get_layout: - * @entry: a #ClutterEntry - * - * Gets the #PangoLayout used to display the entry. - * The layout is useful to e.g. convert text positions to - * pixel positions. - * The returned layout is owned by the entry so need not be - * freed by the caller. - * - * Return value: the #PangoLayout for this entry - * - * Since: 0.4 - **/ -PangoLayout * -clutter_entry_get_layout (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); - - clutter_entry_ensure_layout (entry, -1); - - return entry->priv->layout; -} - -/** - * clutter_entry_set_alignment: - * @entry: a #ClutterEntry - * @alignment: A #PangoAlignment - * - * Sets text alignment of the entry. - * - * Since: 0.4 - */ -void -clutter_entry_set_alignment (ClutterEntry *entry, - PangoAlignment alignment) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->alignment != alignment) - { - g_object_ref (entry); - - priv->alignment = alignment; - - clutter_entry_clear_layout (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - - g_object_notify (G_OBJECT (entry), "alignment"); - g_object_unref (entry); - } -} - -/** - * clutter_entry_get_alignment: - * @entry: a #ClutterEntry - * - * Returns the entry's text alignment - * - * Return value: The entry's #PangoAlignment - * - * Since: 0.4 - */ -PangoAlignment -clutter_entry_get_alignment (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); - - return entry->priv->alignment; -} - -/** - * clutter_entry_set_cursor_position: - * @entry: a #ClutterEntry - * @position: the position of the cursor. - * - * Sets the position of the cursor. The @position must be less than or - * equal to the number of characters in the entry. A value of -1 indicates - * that the position should be set after the last character in the entry. - * Note that this position is in characters, not in bytes. - * - * Since: 0.6 - */ -void -clutter_entry_set_cursor_position (ClutterEntry *entry, - gint position) -{ - ClutterEntryPrivate *priv; - gint len; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->text == NULL) - return; - - len = g_utf8_strlen (priv->text, -1); - - if (position < 0 || position >= len) - priv->position = -1; - else - priv->position = position; - - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_cursor_position: - * @entry: a #ClutterEntry - * - * Gets the position, in characters, of the cursor in @entry. - * - * Return value: the position of the cursor. - * - * Since: 0.6 - */ -gint -clutter_entry_get_cursor_position (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), 0); - - priv = entry->priv; - - return priv->position; -} - -/** - * clutter_entry_handle_key_event: - * @entry: a #ClutterEntry - * @kev: a #ClutterKeyEvent - * - * This function will handle a #ClutterKeyEvent, like those returned in a - * key-press/release-event, and will translate it for the @entry. This includes - * non-alphanumeric keys, such as the arrows keys, which will move the - * input cursor. You should use this function inside a handler for the - * ClutterStage::key-press-event or ClutterStage::key-release-event. - * - * Since: 0.4 - * - * Deprecated: 0.8: The key events will automatically be handled when - * giving the key focus to an entry using clutter_stage_set_key_focus(). - */ -void -clutter_entry_handle_key_event (ClutterEntry *entry, - ClutterKeyEvent *kev) -{ - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - clutter_entry_handle_key_event_internal (entry, kev); -} - -/** - * clutter_entry_insert_unichar: - * @entry: a #ClutterEntry - * @wc: a Unicode character - * - * Insert a character to the right of the current position of the cursor, - * and updates the position of the cursor. - * - * Since: 0.4 - */ -void -clutter_entry_insert_unichar (ClutterEntry *entry, - gunichar wc) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - glong pos; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - g_return_if_fail (g_unichar_validate (wc)); - - if (wc == 0) - return; - - priv = entry->priv; - - g_object_ref (entry); - - new = g_string_new (priv->text); - pos = offset_to_bytes (priv->text, priv->position); - new = g_string_insert_unichar (new, pos, wc); - - clutter_entry_set_text (entry, new->str); - - if (priv->position >= 0) - clutter_entry_set_cursor_position (entry, priv->position + 1); - - g_string_free (new, TRUE); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_delete_chars: - * @entry: a #ClutterEntry - * @len: the number of characters to remove. - * - * Characters are removed from before the current postion of the cursor. - * - * Since: 0.4 - */ -void -clutter_entry_delete_chars (ClutterEntry *entry, - guint num) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - gint len; - gint pos; - gint num_pos; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (!priv->text) - return; - - g_object_ref (entry); - - len = g_utf8_strlen (priv->text, -1); - new = g_string_new (priv->text); - - if (priv->position == -1) - { - num_pos = offset_to_bytes (priv->text, len - num); - new = g_string_erase (new, num_pos, -1); - } - else - { - pos = offset_to_bytes (priv->text, priv->position - num); - num_pos = offset_to_bytes (priv->text, priv->position); - new = g_string_erase (new, pos, num_pos-pos); - } - clutter_entry_set_text (entry, new->str); - - if (priv->position > 0) - clutter_entry_set_cursor_position (entry, priv->position - num); - - g_string_free (new, TRUE); - - g_object_notify (G_OBJECT (entry), "text"); - g_object_unref (entry); -} - -/** - * clutter_entry_insert_text: - * @entry: a #ClutterEntry - * @text: the text to insert - * @position: the position at which to insert the text. - * - * Insert text at a specifc position. - * - * A value of 0 indicates that the text will be inserted before the first - * character in the entry's text, and a value of -1 indicates that the text - * will be inserted after the last character in the entry's text. - * - * Since: 0.4 - */ -void -clutter_entry_insert_text (ClutterEntry *entry, - const gchar *text, - gssize position) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - new = g_string_new (priv->text); - new = g_string_insert (new, position, text); - - clutter_entry_set_text (entry, new->str); - - g_string_free (new, TRUE); -} - -/** - * clutter_entry_delete_text: - * @entry: a #ClutterEntry - * @start_pos: the starting position. - * @end_pos: the end position. - * - * Deletes a sequence of characters. The characters that are deleted are - * those characters at positions from @start_pos up to, but not including, - * @end_pos. If @end_pos is negative, then the characters deleted will be - * those characters from @start_pos to the end of the text. - * - * Since: 0.4 - */ -void -clutter_entry_delete_text (ClutterEntry *entry, - gssize start_pos, - gssize end_pos) -{ - ClutterEntryPrivate *priv; - GString *new = NULL; - gint start_bytes; - gint end_bytes; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (!priv->text) - return; - - start_bytes = offset_to_bytes (priv->text, start_pos); - end_bytes = offset_to_bytes (priv->text, end_pos); - - new = g_string_new (priv->text); - new = g_string_erase (new, start_bytes, end_bytes - start_bytes); - - clutter_entry_set_text (entry, new->str); - - g_string_free (new, TRUE); -} - -/** - * clutter_entry_set_visible_cursor: - * @entry: a #ClutterEntry - * @visible: whether the input cursor should be visible - * - * Sets the visibility of the input cursor. - * - * Since: 0.4 - */ -void -clutter_entry_set_visible_cursor (ClutterEntry *entry, - gboolean visible) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->show_cursor != visible) - { - priv->show_cursor = visible; - - g_object_notify (G_OBJECT (entry), "cursor-visible"); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); - } -} - -/** - * clutter_entry_get_visible_cursor: - * @entry: a #ClutterEntry - * - * Returns the input cursor's visibility - * - * Return value: whether the input cursor is visible - * - * Since: 0.4 - */ -gboolean -clutter_entry_get_visible_cursor (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); - - return entry->priv->show_cursor; -} - -/** - * clutter_entry_set_visibility: - * @entry: a #ClutterEntry - * @visible: %TRUE if the contents of the entry are displayed as plaintext. - * - * Sets whether the contents of the entry are visible or not. When visibility - * is set to %FALSE, characters are displayed as the invisible char, and will - * also appear that way when the text in the entry widget is copied elsewhere. - * - * The default invisible char is the asterisk '*', but it can be changed with - * clutter_entry_set_invisible_char(). - * - * Since: 0.4 - */ -void -clutter_entry_set_visibility (ClutterEntry *entry, gboolean visible) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - priv->text_visible = visible; - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_visibility: - * @entry: a #ClutterEntry - * - * Returns the entry text visibility. - * - * Return value: %TRUE if the contents of the entry are displayed as plaintext. - * - * Since: 0.4 - */ -gboolean -clutter_entry_get_visibility (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); - - return entry->priv->text_visible; -} - -/** - * clutter_entry_set_invisible_char: - * @entry: a #ClutterEntry - * @wc: a Unicode character - * - * Sets the character to use in place of the actual text when - * clutter_entry_set_visibility() has been called to set text visibility - * to %FALSE. i.e. this is the character used in "password mode" to show the - * user how many characters have been typed. The default invisible char is an - * asterisk ('*'). If you set the invisible char to 0, then the user will get - * no feedback at all; there will be no text on the screen as they type. - * - * Since: 0.4 - */ -void -clutter_entry_set_invisible_char (ClutterEntry *entry, gunichar wc) -{ - ClutterEntryPrivate *priv; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - priv->priv_char = wc; - - if (!priv->text_visible) - return; - - clutter_entry_clear_layout (entry); - clutter_entry_clear_cursor_position (entry); - - if (CLUTTER_ACTOR_IS_VISIBLE (entry)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); -} - -/** - * clutter_entry_get_invisible_char: - * @entry: a #ClutterEntry - * - * Returns the character to use in place of the actual text when text-visibility - * is set to %FALSE - * - * Return value: a Unicode character - * - **/ -gunichar -clutter_entry_get_invisible_char (ClutterEntry *entry) -{ - ClutterEntryPrivate *priv; - - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); - - priv = entry->priv; - - return priv->priv_char; -} - -/** - * clutter_entry_set_max_length: - * @entry: a #ClutterEntry - * @max: the maximum number of characters allowed in the entry; 0 - * to disable or -1 to set the length of the current string - * - * Sets the maximum allowed length of the contents of the actor. If the - * current contents are longer than the given length, then they will be - * truncated to fit. - * - * Since: 0.4 - */ -void -clutter_entry_set_max_length (ClutterEntry *entry, - gint max) -{ - ClutterEntryPrivate *priv; - gchar *new = NULL; - - g_return_if_fail (CLUTTER_IS_ENTRY (entry)); - - priv = entry->priv; - - if (priv->max_length != max) - { - g_object_ref (entry); - - if (max < 0) - max = g_utf8_strlen (priv->text, -1); - - priv->max_length = max; - - new = g_strdup (priv->text); - clutter_entry_set_text (entry, new); - g_free (new); - - g_object_notify (G_OBJECT (entry), "max-length"); - g_object_unref (entry); - } -} - -/** - * clutter_entry_get_max_length: - * @entry: a #ClutterEntry - * - * Gets the maximum length of text that can be set into @entry. - * See clutter_entry_set_max_length(). - * - * Return value: the maximum number of characters. - * - * Since: 0.4 - */ -gint -clutter_entry_get_max_length (ClutterEntry *entry) -{ - g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), -1); - - return entry->priv->max_length; -} diff --git a/clutter/clutter-entry.h b/clutter/clutter-entry.h deleted file mode 100644 index b43408522..000000000 --- a/clutter/clutter-entry.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * Neil Jagdish Patel . - */ - -#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __CLUTTER_ENTRY_H__ -#define __CLUTTER_ENTRY_H__ - -#include -#include -#include -#include - - -G_BEGIN_DECLS - -#define CLUTTER_TYPE_ENTRY (clutter_entry_get_type ()) - -#define CLUTTER_ENTRY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_ENTRY, ClutterEntry)) - -#define CLUTTER_ENTRY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_ENTRY, ClutterEntryClass)) - -#define CLUTTER_IS_ENTRY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_ENTRY)) - -#define CLUTTER_IS_ENTRY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_ENTRY)) - -#define CLUTTER_ENTRY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_ENTRY, ClutterEntryClass)) - -typedef struct _ClutterEntry ClutterEntry; -typedef struct _ClutterEntryClass ClutterEntryClass; -typedef struct _ClutterEntryPrivate ClutterEntryPrivate; - -struct _ClutterEntry -{ - /*< private >*/ - ClutterActor parent_instance; - - ClutterEntryPrivate *priv; -}; - -/** - * ClutterEntryClass: - * @paint_cursor: virtual function for subclasses to use to draw a custom - * cursor instead of the default one - * @text_changed: signal class handler for ClutterEntry::text-changed - * @cursor_event: signal class handler for ClutterEntry::cursor-event - * @activate: signal class handler for ClutterEntry::activate - * - * Class fo entry actors. - * - * Since: 0.4 - */ -struct _ClutterEntryClass -{ - /*< private >*/ - ClutterActorClass parent_class; - - /*< public >*/ - /* vfuncs, not signals */ - void (* paint_cursor) (ClutterEntry *entry); - - /* signals */ - void (* text_changed) (ClutterEntry *entry); - void (* cursor_event) (ClutterEntry *entry, - ClutterGeometry *geometry); - void (* activate) (ClutterEntry *entry); - - /*< private >*/ - /* padding for future */ - void (*_clutter_entry_1) (void); - void (*_clutter_entry_2) (void); - void (*_clutter_entry_3) (void); - void (*_clutter_entry_4) (void); -}; - -GType clutter_entry_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_entry_new (void); -ClutterActor * clutter_entry_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); -ClutterActor * clutter_entry_new_with_text (const gchar *font_name, - const gchar *text); -void clutter_entry_set_text (ClutterEntry *entry, - const gchar *text); -G_CONST_RETURN gchar *clutter_entry_get_text (ClutterEntry *entry); -void clutter_entry_set_font_name (ClutterEntry *entry, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_entry_get_font_name (ClutterEntry *entry); -void clutter_entry_set_color (ClutterEntry *entry, - const ClutterColor *color); -void clutter_entry_get_color (ClutterEntry *entry, - ClutterColor *color); -PangoLayout * clutter_entry_get_layout (ClutterEntry *entry); -void clutter_entry_set_alignment (ClutterEntry *entry, - PangoAlignment alignment); -PangoAlignment clutter_entry_get_alignment (ClutterEntry *entry); -void clutter_entry_set_cursor_position (ClutterEntry *entry, - gint position); -gint clutter_entry_get_cursor_position (ClutterEntry *entry); -void clutter_entry_insert_unichar (ClutterEntry *entry, - gunichar wc); -void clutter_entry_delete_chars (ClutterEntry *entry, - guint len); -void clutter_entry_insert_text (ClutterEntry *entry, - const gchar *text, - gssize position); -void clutter_entry_delete_text (ClutterEntry *entry, - gssize start_pos, - gssize end_pos); -void clutter_entry_set_visible_cursor (ClutterEntry *entry, - gboolean visible); -gboolean clutter_entry_get_visible_cursor (ClutterEntry *entry); - -void clutter_entry_set_visibility (ClutterEntry *entry, - gboolean visible); -gboolean clutter_entry_get_visibility (ClutterEntry *entry); -void clutter_entry_set_invisible_char (ClutterEntry *entry, - gunichar wc); -gunichar clutter_entry_get_invisible_char (ClutterEntry *entry); -void clutter_entry_set_max_length (ClutterEntry *entry, - gint max); -gint clutter_entry_get_max_length (ClutterEntry *entry); - -#ifndef CLUTTER_DISABLE_DEPRECATED -void clutter_entry_handle_key_event (ClutterEntry *entry, - ClutterKeyEvent *kev); -#endif - -G_END_DECLS - -#endif /* __CLUTTER_ENTRY_H__ */ diff --git a/clutter/clutter-label.c b/clutter/clutter-label.c deleted file mode 100644 index 74434e246..000000000 --- a/clutter/clutter-label.c +++ /dev/null @@ -1,1347 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:clutter-label - * @short_description: Actor for displaying text - * - * #ClutterLabel is a #ClutterActor that displays text using Pango. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "clutter-label.h" -#include "clutter-main.h" -#include "clutter-enum-types.h" -#include "clutter-private.h" -#include "clutter-debug.h" -#include "clutter-units.h" - -#include "cogl-pango.h" - -#define DEFAULT_FONT_NAME "Sans 10" - -G_DEFINE_TYPE (ClutterLabel, clutter_label, CLUTTER_TYPE_ACTOR) - -/* Probably move into main */ -static PangoContext *_context = NULL; - -static const ClutterColor default_text_color = { 0, 0, 0, 255 }; - -enum -{ - PROP_0, - PROP_FONT_NAME, - PROP_TEXT, - PROP_COLOR, - PROP_ATTRIBUTES, - PROP_USE_MARKUP, - PROP_ALIGNMENT, - PROP_WRAP, - PROP_WRAP_MODE, - PROP_JUSTIFY, - PROP_ELLIPSIZE, -}; - -#define CLUTTER_LABEL_GET_PRIVATE(obj) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_LABEL, ClutterLabelPrivate)) - -typedef struct _ClutterLabelCachedLayout ClutterLabelCachedLayout; - -struct _ClutterLabelCachedLayout -{ - /* Cached layout. Pango internally caches the computed extents when - they are requested so there is no need to cache that as well */ - PangoLayout *layout; - /* The width that used to generate this layout */ - ClutterUnit width; - /* A number representing the age of this cache (so that when a new - layout is needed the last used cache is replaced) */ - guint age; -}; - -/* We need at least three cached layouts to run the allocation without - regenerating a new layout. First the layout will be generated at - full width to get the preferred width, then it will be generated at - the preferred width to get the preferred height and then it might - be regenerated at a different width to get the height for the - actual allocated width */ -#define CLUTTER_LABEL_N_CACHED_LAYOUTS 3 - -struct _ClutterLabelPrivate -{ - PangoFontDescription *font_desc; - - ClutterColor fgcol; - - gchar *text; - gchar *font_name; - - PangoAttrList *attrs; - PangoAttrList *effective_attrs; - - ClutterLabelCachedLayout cached_layouts[CLUTTER_LABEL_N_CACHED_LAYOUTS]; - guint cache_age; - - guint alignment : 2; - guint wrap : 1; - guint use_underline : 1; - guint use_markup : 1; - guint ellipsize : 3; - guint single_line_mode : 1; - guint wrap_mode : 3; - guint justify : 1; -}; - -/* - * clutter_label_create_layout_no_cache: - * @label: a #ClutterLabel - * @allocation_width: the width of the layout, or -1 - * - * Creates a new #PangoLayout for the given @allocation_width, using - * the layout properties of the @label. - * - * This function will not touch the glyphs cache. - * - * This function should be used by clutter_label_get_preferred_width() - * and clutter_label_get_preferred_height(). - */ -static PangoLayout * -clutter_label_create_layout_no_cache (ClutterLabel *label, - ClutterUnit allocation_width) -{ - ClutterLabelPrivate *priv = label->priv; - PangoLayout *layout; - - layout = pango_layout_new (_context); - - if (priv->effective_attrs) - pango_layout_set_attributes (layout, priv->effective_attrs); - - pango_layout_set_alignment (layout, priv->alignment); - pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); - - pango_layout_set_font_description (layout, priv->font_desc); - pango_layout_set_justify (layout, priv->justify); - - if (priv->text) - { - if (!priv->use_markup) - pango_layout_set_text (layout, priv->text, -1); - else - pango_layout_set_markup (layout, priv->text, -1); - } - - if (allocation_width > 0 && - (priv->ellipsize != PANGO_ELLIPSIZE_NONE || priv->wrap)) - { - int layout_width, layout_height; - - pango_layout_get_size (layout, &layout_width, &layout_height); - - /* No need to set ellipsize or wrap if we already have enough - * space, since we don't want to make the layout wider than it - * would be otherwise. - */ - - if (CLUTTER_UNITS_FROM_PANGO_UNIT (layout_width) > allocation_width) - { - if (priv->ellipsize != PANGO_ELLIPSIZE_NONE) - { - gint width; - - width = allocation_width > 0 - ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) - : -1; - - pango_layout_set_ellipsize (layout, priv->ellipsize); - pango_layout_set_width (layout, width); - } - else if (priv->wrap) - { - gint width; - - width = allocation_width > 0 - ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width) - : -1; - - pango_layout_set_wrap (layout, priv->wrap_mode); - pango_layout_set_width (layout, width); - } - } - } - - return layout; -} - -static void -clutter_label_dirty_cache (ClutterLabel *label) -{ - ClutterLabelPrivate *priv = label->priv; - int i; - - /* Delete the cached layouts so they will be recreated the next time - they are needed */ - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - if (priv->cached_layouts[i].layout) - { - g_object_unref (priv->cached_layouts[i].layout); - priv->cached_layouts[i].layout = NULL; - } -} - -/* - * clutter_label_create_layout: - * @label: a #ClutterLabel - * @allocation_width: the allocation width - * - * Like clutter_label_create_layout_no_cache(), but will also ensure - * the glyphs cache. If a previously cached layout generated using the - * same width is available then that will be used instead of - * generating a new one. - */ -static PangoLayout * -clutter_label_create_layout (ClutterLabel *label, - ClutterUnit allocation_width) -{ - ClutterLabelPrivate *priv = label->priv; - int i; - ClutterLabelCachedLayout *oldest_cache = priv->cached_layouts; - gboolean found_free_cache = FALSE; - - /* Search for a cached layout with the same width and keep track of - the oldest one */ - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - { - if (priv->cached_layouts[i].layout == NULL) - { - /* Always prefer free cache spaces */ - found_free_cache = TRUE; - oldest_cache = priv->cached_layouts + i; - } - /* If this cached layout is using the same width then we can - just return that directly */ - else if (priv->cached_layouts[i].width == allocation_width) - { - CLUTTER_NOTE (ACTOR, "ClutterLabel: %p: cache hit for width %i", - label, CLUTTER_UNITS_TO_DEVICE (allocation_width)); - return priv->cached_layouts[i].layout; - } - else if (!found_free_cache && (priv->cached_layouts[i].age - < oldest_cache->age)) - oldest_cache = priv->cached_layouts + i; - } - - CLUTTER_NOTE (ACTOR, "ClutterLabel: %p: cache miss for width %i", - label, CLUTTER_UNITS_TO_DEVICE (allocation_width)); - - /* If we make it here then we didn't have a cached version so we - need to recreate the layout */ - if (oldest_cache->layout) - g_object_unref (oldest_cache->layout); - - oldest_cache->layout - = clutter_label_create_layout_no_cache (label, allocation_width); - - cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout); - - /* Mark the 'time' this cache was created and advance the time */ - oldest_cache->age = priv->cache_age++; - - oldest_cache->width = allocation_width; - - return oldest_cache->layout; -} - -static void -clutter_label_paint (ClutterActor *self) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterLabelPrivate *priv = label->priv; - PangoLayout *layout; - ClutterActorBox alloc = { 0, }; - CoglColor color = { 0, }; - - if (priv->font_desc == NULL || priv->text == NULL) - { - CLUTTER_NOTE (ACTOR, "desc: %p, text %p", - priv->font_desc ? priv->font_desc : 0x0, - priv->text ? priv->text : 0x0); - return; - } - - CLUTTER_NOTE (PAINT, "painting label (text:`%s')", priv->text); - - clutter_actor_get_allocation_box (self, &alloc); - layout = clutter_label_create_layout (label, alloc.x2 - alloc.x1); - - cogl_color_set_from_4ub (&color, - priv->fgcol.red, - priv->fgcol.green, - priv->fgcol.blue, - clutter_actor_get_paint_opacity (self)); - - cogl_pango_render_layout (layout, 0, 0, &color, 0); -} - -static void -clutter_label_get_preferred_width (ClutterActor *self, - ClutterUnit for_height, - ClutterUnit *min_width_p, - ClutterUnit *natural_width_p) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterLabelPrivate *priv = label->priv; - PangoRectangle logical_rect = { 0, }; - PangoLayout *layout; - ClutterUnit layout_width; - - layout = clutter_label_create_layout (label, -1); - - pango_layout_get_extents (layout, NULL, &logical_rect); - - layout_width = logical_rect.width > 0 - ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.width) - : 1; - - if (min_width_p) - { - if (priv->wrap || priv->ellipsize) - *min_width_p = 1; - else - *min_width_p = layout_width; - } - - if (natural_width_p) - *natural_width_p = layout_width; -} - -static void -clutter_label_get_preferred_height (ClutterActor *self, - ClutterUnit for_width, - ClutterUnit *min_height_p, - ClutterUnit *natural_height_p) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - - if (for_width == 0) - { - if (min_height_p) - *min_height_p = 0; - - if (natural_height_p) - *natural_height_p = 0; - } - else - { - PangoLayout *layout; - PangoRectangle logical_rect = { 0, }; - ClutterUnit height; - - layout = clutter_label_create_layout (label, for_width); - - pango_layout_get_extents (layout, NULL, &logical_rect); - height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.height); - - if (min_height_p) - *min_height_p = height; - - if (natural_height_p) - *natural_height_p = height; - } -} - -static void -clutter_label_allocate (ClutterActor *self, - const ClutterActorBox *box, - gboolean origin_changed) -{ - ClutterLabel *label = CLUTTER_LABEL (self); - ClutterActorClass *parent_class; - - /* Ensure that there is a cached label with the right width so that - we don't need to create the label during the paint run */ - clutter_label_create_layout (label, box->x2 - box->x1); - - parent_class = CLUTTER_ACTOR_CLASS (clutter_label_parent_class); - parent_class->allocate (self, box, origin_changed); -} - -static void -clutter_label_dispose (GObject *object) -{ - ClutterLabel *self = CLUTTER_LABEL(object); - ClutterLabelPrivate *priv; - - priv = self->priv; - - /* Get rid of the cached layouts */ - clutter_label_dirty_cache (self); - - G_OBJECT_CLASS (clutter_label_parent_class)->dispose (object); -} - -static void -clutter_label_finalize (GObject *object) -{ - ClutterLabel *self = CLUTTER_LABEL(object); - ClutterLabelPrivate *priv; - - priv = self->priv; - - if (priv->font_desc) - pango_font_description_free (priv->font_desc); - - g_free (priv->text); - g_free (priv->font_name); - - G_OBJECT_CLASS (clutter_label_parent_class)->finalize (object); -} - -static void -clutter_label_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterLabel *label; - ClutterLabelPrivate *priv; - - label = CLUTTER_LABEL(object); - priv = label->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - clutter_label_set_font_name (label, g_value_get_string (value)); - break; - case PROP_TEXT: - clutter_label_set_text (label, g_value_get_string (value)); - break; - case PROP_COLOR: - clutter_label_set_color (label, clutter_value_get_color (value)); - break; - case PROP_ATTRIBUTES: - clutter_label_set_attributes (label, g_value_get_boxed (value)); - break; - case PROP_ALIGNMENT: - clutter_label_set_alignment (label, g_value_get_enum (value)); - break; - case PROP_USE_MARKUP: - clutter_label_set_use_markup (label, g_value_get_boolean (value)); - break; - case PROP_WRAP: - clutter_label_set_line_wrap (label, g_value_get_boolean (value)); - break; - case PROP_JUSTIFY: - clutter_label_set_justify (label, g_value_get_boolean (value)); - break; - case PROP_WRAP_MODE: - clutter_label_set_line_wrap_mode (label, g_value_get_enum (value)); - break; - case PROP_ELLIPSIZE: - clutter_label_set_ellipsize (label, g_value_get_enum (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_label_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterLabel *label; - ClutterLabelPrivate *priv; - - label = CLUTTER_LABEL (object); - priv = label->priv; - - switch (prop_id) - { - case PROP_FONT_NAME: - g_value_set_string (value, priv->font_name); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_COLOR: - clutter_value_set_color (value, &priv->fgcol); - break; - case PROP_ATTRIBUTES: - g_value_set_boxed (value, priv->attrs); - break; - case PROP_ALIGNMENT: - g_value_set_enum (value, priv->alignment); - break; - case PROP_USE_MARKUP: - g_value_set_boolean (value, priv->use_markup); - break; - case PROP_JUSTIFY: - g_value_set_boolean (value, priv->justify); - break; - case PROP_WRAP: - g_value_set_boolean (value, priv->wrap); - break; - case PROP_WRAP_MODE: - g_value_set_enum (value, priv->wrap_mode); - break; - case PROP_ELLIPSIZE: - g_value_set_enum (value, priv->ellipsize); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -clutter_label_class_init (ClutterLabelClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; - - actor_class->paint = clutter_label_paint; - actor_class->get_preferred_width = clutter_label_get_preferred_width; - actor_class->get_preferred_height = clutter_label_get_preferred_height; - actor_class->allocate = clutter_label_allocate; - - gobject_class->finalize = clutter_label_finalize; - gobject_class->dispose = clutter_label_dispose; - gobject_class->set_property = clutter_label_set_property; - gobject_class->get_property = clutter_label_get_property; - - /** - * ClutterLabel:font-name: - * - * The font to be used by the #ClutterLabel, as a string - * that can be parsed by pango_font_description_from_string(). - * - * Since: 0.2 - */ - pspec = g_param_spec_string ("font-name", - "Font Name", - "The font to be used by the label", - NULL, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); - - pspec = g_param_spec_string ("text", - "Text", - "The text to render", - NULL, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_TEXT, pspec); - - pspec = clutter_param_spec_color ("color", - "Font Color", - "Color of the font used by the label", - &default_text_color, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_COLOR, pspec); - - pspec = g_param_spec_boxed ("attributes", - "Attributes", - "A list of style attributes to apply to " - "the text of the label", - PANGO_TYPE_ATTR_LIST, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec); - - /** - * ClutterLabel:use-markup: - * - * Whether the text of the label includes Pango markup. See - * pango_layout_set_markup() in the Pango documentation. - * - * Since: 0.2 - */ - pspec = g_param_spec_boolean ("use-markup", - "Use markup", - "Whether or not the text of the label " - "includes Pango markup", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); - - /** - * ClutterLabel:wrap: - * - * Whether to wrap the lines of #ClutterLabel:text if the contents - * exceed the available allocation. The wrapping strategy is - * controlled by the #ClutterLabel:wrap-mode property. - * - * Since: 0.2 - */ - pspec = g_param_spec_boolean ("wrap", - "Line wrap", - "If set, wrap the lines if the text " - "becomes too wide", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_WRAP, pspec); - - /** - * ClutterLabel:wrap-mode: - * - * If #ClutterLabel:wrap is set to %TRUE, this property will - * control how the text is wrapped. - * - * Since: 0.2 - */ - pspec = g_param_spec_enum ("wrap-mode", - "Line wrap mode", - "Control how line-wrapping is done", - PANGO_TYPE_WRAP_MODE, - PANGO_WRAP_WORD, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_WRAP_MODE, pspec); - - pspec = g_param_spec_enum ("ellipsize", - "Ellipsize", - "The preferred place to ellipsize the string, " - "if the label does not have enough room to " - "display the entire string", - PANGO_TYPE_ELLIPSIZE_MODE, - PANGO_ELLIPSIZE_NONE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec); - - /** - * ClutterLabel:alignment: - * - * The preferred alignment for the text. This property controls - * the alignment of multi-line paragraphs. - * - * Since: 0.2 - */ - pspec = g_param_spec_enum ("alignment", - "Alignment", - "The preferred alignment for the string, " - "for multi-line text", - PANGO_TYPE_ALIGNMENT, - PANGO_ALIGN_LEFT, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ALIGNMENT, pspec); - - /** - * ClutterLabel:justify: - * - * Whether the contents of the label should be justified on both - * margins. - * - * Since: 0.6 - */ - pspec = g_param_spec_boolean ("justify", - "Justify", - "Whether the contents of the label " - "should be justified", - FALSE, - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); - - g_type_class_add_private (gobject_class, sizeof (ClutterLabelPrivate)); -} - -static void -clutter_label_init (ClutterLabel *self) -{ - ClutterLabelPrivate *priv; - int i; - - self->priv = priv = CLUTTER_LABEL_GET_PRIVATE (self); - - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - - priv->alignment = PANGO_ALIGN_LEFT; - priv->wrap = FALSE; - priv->wrap_mode = PANGO_WRAP_WORD; - priv->ellipsize = PANGO_ELLIPSIZE_NONE; - priv->use_underline = FALSE; - priv->use_markup = FALSE; - priv->justify = FALSE; - - for (i = 0; i < CLUTTER_LABEL_N_CACHED_LAYOUTS; i++) - priv->cached_layouts[i].layout = NULL; - - priv->text = NULL; - priv->attrs = NULL; - - priv->fgcol = default_text_color; - - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->font_desc = pango_font_description_from_string (priv->font_name); -} - -/** - * clutter_label_new_with_text: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * - * Creates a new #ClutterLabel displaying @text using @font_name. - * - * Return value: a #ClutterLabel - */ -ClutterActor* -clutter_label_new_with_text (const gchar *font_name, - const gchar *text) -{ - return g_object_new (CLUTTER_TYPE_LABEL, - "font-name", font_name, - "text", text, - NULL); -} - -/** - * clutter_label_new_full: - * @font_name: the name (and size) of the font to be used - * @text: the text to be displayed - * @color: #ClutterColor for text - * - * Creates a new #ClutterLabel displaying @text with @color - * using @font_name. - * - * Return value: a #ClutterLabel - */ -ClutterActor* -clutter_label_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color) -{ - return g_object_new (CLUTTER_TYPE_LABEL, - "font-name", font_name, - "text", text, - "color", color, - NULL); -} - -/** - * clutter_label_new: - * - * Creates a new, empty #ClutterLabel. - * - * Returns: the newly created #ClutterLabel - */ -ClutterActor * -clutter_label_new (void) -{ - return g_object_new (CLUTTER_TYPE_LABEL, NULL); -} - -/** - * clutter_label_get_text: - * @label: a #ClutterLabel - * - * Retrieves the text displayed by @label - * - * Return value: the text of the label. The returned string is - * owned by #ClutterLabel and should not be modified or freed. - */ -G_CONST_RETURN gchar * -clutter_label_get_text (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->text; -} - -/** - * clutter_label_set_text: - * @label: a #ClutterLabel - * @text: the text to be displayed - * - * Sets @text as the text to be displayed by @label. - */ -void -clutter_label_set_text (ClutterLabel *label, - const gchar *text) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - g_free (priv->text); - - priv->text = g_strdup (text); - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "text"); -} - -/** - * clutter_label_get_font_name: - * @label: a #ClutterLabel - * - * Retrieves the font used by @label. - * - * Return value: a string containing the font name, in a format - * understandable by pango_font_description_from_string(). The - * string is owned by @label and should not be modified - * or freed. - */ -G_CONST_RETURN gchar * -clutter_label_get_font_name (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->font_name; -} - -/** - * clutter_label_set_font_name: - * @label: a #ClutterLabel - * @font_name: a font name and size, or %NULL for the default font - * - * Sets @font_name as the font used by @label. - * - * @font_name must be a string containing the font name and its - * size, similarly to what you would feed to the - * pango_font_description_from_string() function. - */ -void -clutter_label_set_font_name (ClutterLabel *label, - const gchar *font_name) -{ - ClutterLabelPrivate *priv; - PangoFontDescription *desc; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; - - priv = label->priv; - - if (strcmp (priv->font_name, font_name) == 0) - return; - - desc = pango_font_description_from_string (font_name); - if (!desc) - { - g_warning ("Attempting to create a PangoFontDescription for " - "font name `%s', but failed.", - font_name); - return; - } - - g_free (priv->font_name); - priv->font_name = g_strdup (font_name); - - if (priv->font_desc) - pango_font_description_free (priv->font_desc); - - priv->font_desc = desc; - - clutter_label_dirty_cache (label); - - if (label->priv->text && label->priv->text[0] != '\0') - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "font-name"); -} - - -/** - * clutter_label_set_color: - * @label: a #ClutterLabel - * @color: a #ClutterColor - * - * Sets the color of @label. - */ -void -clutter_label_set_color (ClutterLabel *label, - const ClutterColor *color) -{ - ClutterActor *actor; - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (color != NULL); - - priv = label->priv; - - g_object_ref (label); - - priv->fgcol.red = color->red; - priv->fgcol.green = color->green; - priv->fgcol.blue = color->blue; - priv->fgcol.alpha = color->alpha; - - actor = CLUTTER_ACTOR (label); - - clutter_actor_set_opacity (actor, priv->fgcol.alpha); - - if (CLUTTER_ACTOR_IS_VISIBLE (actor)) - clutter_actor_queue_redraw (actor); - - g_object_notify (G_OBJECT (label), "color"); - g_object_unref (label); -} - -/** - * clutter_label_get_color: - * @label: a #ClutterLabel - * @color: return location for a #ClutterColor - * - * Retrieves the color of @label. - */ -void -clutter_label_get_color (ClutterLabel *label, - ClutterColor *color) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (color != NULL); - - priv = label->priv; - - color->red = priv->fgcol.red; - color->green = priv->fgcol.green; - color->blue = priv->fgcol.blue; - color->alpha = priv->fgcol.alpha; -} - -/** - * clutter_label_set_ellipsize: - * @label: a #ClutterLabel - * @mode: a #PangoEllipsizeMode - * - * Sets the mode used to ellipsize (add an ellipsis: "...") to the text - * if there is not enough space to render the entire string. - * - * Since: 0.2 - **/ -void -clutter_label_set_ellipsize (ClutterLabel *label, - PangoEllipsizeMode mode) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && - mode <= PANGO_ELLIPSIZE_END); - - priv = label->priv; - - if ((PangoEllipsizeMode) priv->ellipsize != mode) - { - priv->ellipsize = mode; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "ellipsize"); - } -} - -/** - * clutter_label_get_ellipsize: - * @label: a #ClutterLabel - * - * Returns the ellipsizing position of the label. - * See clutter_label_set_ellipsize(). - * - * Return value: #PangoEllipsizeMode - * - * Since: 0.2 - **/ -PangoEllipsizeMode -clutter_label_get_ellipsize (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), PANGO_ELLIPSIZE_NONE); - - return label->priv->ellipsize; -} - -/** - * clutter_label_set_line_wrap: - * @label: a #ClutterLabel - * @wrap: the setting - * - * Toggles line wrapping within the #ClutterLabel widget. %TRUE makes - * it break lines if text exceeds the widget's size. %FALSE lets the - * text get cut off by the edge of the widget if it exceeds the widget - * size. - * - * Since: 0.2 - */ -void -clutter_label_set_line_wrap (ClutterLabel *label, - gboolean wrap) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - wrap = wrap != FALSE; - - if (priv->wrap != wrap) - { - priv->wrap = wrap; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "wrap"); - } -} - -/** - * clutter_label_get_line_wrap: - * @label: a #ClutterLabel - * - * Returns whether lines in the label are automatically wrapped. - * See clutter_label_set_line_wrap (). - * - * Return value: %TRUE if the lines of the label are automatically wrapped. - * - * Since: 0.2 - */ -gboolean -clutter_label_get_line_wrap (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->wrap; -} - -/** - * clutter_label_set_line_wrap_mode: - * @label: a #ClutterLabel - * @wrap_mode: the line wrapping mode - * - * If line wrapping is on (see clutter_label_set_line_wrap()) this controls how - * the line wrapping is done. The default is %PANGO_WRAP_WORD which means - * wrap on word boundaries. - * - * Since: 0.2 - **/ -void -clutter_label_set_line_wrap_mode (ClutterLabel *label, - PangoWrapMode wrap_mode) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->wrap_mode != wrap_mode) - { - priv->wrap_mode = wrap_mode; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "wrap-mode"); - } -} - -/** - * clutter_label_get_line_wrap_mode: - * @label: a #ClutterLabel - * - * Returns line wrap mode used by the label. - * See clutter_label_set_line_wrap_mode (). - * - * Return value: %TRUE if the lines of the label are automatically wrapped. - * - * Since: 0.2 - */ -PangoWrapMode -clutter_label_get_line_wrap_mode (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->wrap_mode; -} - -/** - * clutter_label_get_layout: - * @label: a #ClutterLabel - * - * Gets the #PangoLayout used to display the label. - * The layout is useful to e.g. convert text positions to - * pixel positions. - * The returned layout is owned by the label so need not be - * freed by the caller. - * - * Return value: the #PangoLayout for this label - * - * Since: 0.2 - **/ -PangoLayout * -clutter_label_get_layout (ClutterLabel *label) -{ - ClutterUnit width; - - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - width = clutter_actor_get_widthu (CLUTTER_ACTOR (label)); - - return clutter_label_create_layout (label, width); -} - -static inline void -clutter_label_set_attributes_internal (ClutterLabel *label, - PangoAttrList *attrs) -{ -} - -/** - * clutter_label_set_attributes: - * @label: a #ClutterLabel - * @attrs: a #PangoAttrList - * - * Sets a #PangoAttrList; the attributes in the list are applied to the - * label text. The attributes set with this function will be ignored - * if the "use_markup" property - * is %TRUE. - * - * Since: 0.2 - **/ -void -clutter_label_set_attributes (ClutterLabel *label, - PangoAttrList *attrs) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (attrs) - pango_attr_list_ref (attrs); - - if (priv->attrs) - pango_attr_list_unref (priv->attrs); - - if (!priv->use_markup) - { - if (attrs) - pango_attr_list_ref (attrs); - - if (priv->effective_attrs) - pango_attr_list_unref (priv->effective_attrs); - - priv->effective_attrs = attrs; - } - - priv->attrs = attrs; - - clutter_label_dirty_cache (label); - - g_object_notify (G_OBJECT (label), "attributes"); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); -} - -/** - * clutter_label_get_attributes: - * @label: a #ClutterLabel - * - * Gets the attribute list that was set on the label using - * clutter_label_set_attributes(), if any. - * - * Return value: the attribute list, or %NULL if none was set. - * - * Since: 0.2 - **/ -PangoAttrList * -clutter_label_get_attributes (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), NULL); - - return label->priv->attrs; -} - -/** - * clutter_label_set_use_markup: - * @label: a #ClutterLabel - * @setting: %TRUE if the label's text should be parsed for markup. - * - * Sets whether the text of the label contains markup in Pango's text markup - * language. - **/ -void -clutter_label_set_use_markup (ClutterLabel *label, - gboolean setting) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->use_markup != setting) - { - priv->use_markup = setting; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "use-markup"); - } -} - -/** - * clutter_label_get_use_markup: - * @label: a #ClutterLabel - * - * Returns whether the label's text is interpreted as marked up with - * the Pango text markup - * language. See clutter_label_set_use_markup (). - * - * Return value: %TRUE if the label's text will be parsed for markup. - **/ -gboolean -clutter_label_get_use_markup (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->use_markup; -} - -/** - * clutter_label_set_alignment: - * @label: a #ClutterLabel - * @alignment: A #PangoAlignment - * - * Sets text alignment of the label. - * - * The alignment will only be used when the contents of the - * label are enough to wrap, and the #ClutterLabel:wrap - * property is set to %TRUE. - **/ -void -clutter_label_set_alignment (ClutterLabel *label, - PangoAlignment alignment) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->alignment != alignment) - { - priv->alignment = alignment; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "alignment"); - } -} - -/** - * clutter_label_get_alignment: - * @label: a #ClutterLabel - * - * Returns the label's text alignment - * - * Return value: The label's #PangoAlignment - * - * Since: 0.2 - **/ -PangoAlignment -clutter_label_get_alignment (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->alignment; -} - -/** - * clutter_label_set_justify: - * @label: a #ClutterLabel - * @justify: whether the text should be justified - * - * Sets whether the text of the @label actor should be justified - * on both margins. This setting is ignored if Clutter is compiled - * against Pango < 1.18. - * - * Since: 0.6 - */ -void -clutter_label_set_justify (ClutterLabel *label, - gboolean justify) -{ - ClutterLabelPrivate *priv; - - g_return_if_fail (CLUTTER_IS_LABEL (label)); - - priv = label->priv; - - if (priv->justify != justify) - { - priv->justify = justify; - - clutter_label_dirty_cache (label); - - clutter_actor_queue_relayout (CLUTTER_ACTOR (label)); - - g_object_notify (G_OBJECT (label), "justify"); - } -} - -/** - * clutter_label_get_justify: - * @label: a #ClutterLabel - * - * Retrieves whether the label should justify the text on both margins. - * - * Return value: %TRUE if the text should be justified - * - * Since: 0.6 - */ -gboolean -clutter_label_get_justify (ClutterLabel *label) -{ - g_return_val_if_fail (CLUTTER_IS_LABEL (label), FALSE); - - return label->priv->justify; -} diff --git a/clutter/clutter-label.h b/clutter/clutter-label.h deleted file mode 100644 index d6021c2f3..000000000 --- a/clutter/clutter-label.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Authored By Matthew Allum - * - * Copyright (C) 2006 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __CLUTTER_LABEL_H__ -#define __CLUTTER_LABEL_H__ - -#include -#include -#include - - -G_BEGIN_DECLS - -#define CLUTTER_TYPE_LABEL (clutter_label_get_type ()) - -#define CLUTTER_LABEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_LABEL, ClutterLabel)) - -#define CLUTTER_LABEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_LABEL, ClutterLabelClass)) - -#define CLUTTER_IS_LABEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_LABEL)) - -#define CLUTTER_IS_LABEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_LABEL)) - -#define CLUTTER_LABEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_LABEL, ClutterLabelClass)) - -typedef struct _ClutterLabel ClutterLabel; -typedef struct _ClutterLabelClass ClutterLabelClass; -typedef struct _ClutterLabelPrivate ClutterLabelPrivate; - -struct _ClutterLabel -{ - ClutterActor parent; - - /*< private >*/ - ClutterLabelPrivate *priv; -}; - -struct _ClutterLabelClass -{ - /*< private >*/ - ClutterActorClass parent_class; - - void (*_clutter_label_1) (void); - void (*_clutter_label_2) (void); - void (*_clutter_label_3) (void); - void (*_clutter_label_4) (void); -}; - -GType clutter_label_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_label_new (void); - -ClutterActor* clutter_label_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); - -ClutterActor * clutter_label_new_with_text (const gchar *font_name, - const gchar *text); -void clutter_label_set_text (ClutterLabel *label, - const gchar *text); -G_CONST_RETURN gchar *clutter_label_get_text (ClutterLabel *label); -void clutter_label_set_font_name (ClutterLabel *label, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_label_get_font_name (ClutterLabel *label); -void clutter_label_set_color (ClutterLabel *label, - const ClutterColor *color); -void clutter_label_get_color (ClutterLabel *label, - ClutterColor *color); -void clutter_label_set_ellipsize (ClutterLabel *label, - PangoEllipsizeMode mode); -PangoEllipsizeMode clutter_label_get_ellipsize (ClutterLabel *label); -void clutter_label_set_line_wrap (ClutterLabel *label, - gboolean wrap); -gboolean clutter_label_get_line_wrap (ClutterLabel *label); -void clutter_label_set_line_wrap_mode (ClutterLabel *label, - PangoWrapMode wrap_mode); -PangoWrapMode clutter_label_get_line_wrap_mode (ClutterLabel *label); -PangoLayout * clutter_label_get_layout (ClutterLabel *label); -void clutter_label_set_attributes (ClutterLabel *label, - PangoAttrList *attrs); -PangoAttrList * clutter_label_get_attributes (ClutterLabel *label); -void clutter_label_set_use_markup (ClutterLabel *label, - gboolean setting); -gboolean clutter_label_get_use_markup (ClutterLabel *label); -void clutter_label_set_alignment (ClutterLabel *label, - PangoAlignment alignment); -PangoAlignment clutter_label_get_alignment (ClutterLabel *label); -void clutter_label_set_justify (ClutterLabel *label, - gboolean justify); -gboolean clutter_label_get_justify (ClutterLabel *label); - -G_END_DECLS - -#endif /* __CLUTTER_LABEL_H__ */ From 2d166b250c0e0da33171f0d2f87735c965af50e9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 15:10:55 +0000 Subject: [PATCH 051/108] [docs] Add annotations for ClutterText structures ClutterText and ClutterTextClass were missing annotations for gtk-doc to pick up. --- clutter/clutter-text.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 56595b794..5598b0980 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -45,6 +45,13 @@ typedef struct _ClutterText ClutterText; typedef struct _ClutterTextPrivate ClutterTextPrivate; typedef struct _ClutterTextClass ClutterTextClass; +/** + * ClutterText: + * + * The #ClutterText struct contains only private data. + * + * Since: 1.0 + */ struct _ClutterText { /*< private >*/ @@ -53,6 +60,16 @@ struct _ClutterText ClutterTextPrivate *priv; }; +/** + * ClutterTextClass: + * @text_changed: class handler for the #ClutterText::text-changed signal + * @activate: class handler for the #ClutterText::activate signal + * @cursor_event: class handler for the #ClutterText::cursor_event signal + * + * The #ClutterTextClass struct contains only private data. + * + * Since: 1.0 + */ struct _ClutterTextClass { /*< private >*/ From 2714397fd517636461088f20521c80801b696b60 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 18:23:25 +0000 Subject: [PATCH 052/108] Constify the cursor geometry in ::cursor-event We don't allow changing the cursor geometry inside the ::cursor-event signal handlers; for starters, it would make binding the signal a huge mess, and it would also potentially break the whole actor. --- clutter/clutter-text.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 5598b0980..4001181b5 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -76,10 +76,11 @@ struct _ClutterTextClass ClutterActorClass parent_class; /*< public >*/ - void (* text_changed) (ClutterText *self); - void (* activate) (ClutterText *self); - void (* cursor_event) (ClutterText *self, - ClutterGeometry *geometry); + /* signals, not vfuncs */ + void (* text_changed) (ClutterText *self); + void (* activate) (ClutterText *self); + void (* cursor_event) (ClutterText *self, + const ClutterGeometry *geometry); /*< private >*/ /* padding for future expansion */ From e615b8cc6bd6e77cc649e71db6ea62248e37a6cd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 18:24:42 +0000 Subject: [PATCH 053/108] Use units in position_to_coords() We loose precision with a direct conversion for PangoUnits to pixels, so we should do the conversion as needed, inside the callers of clutter_text_position_to_coords(). --- clutter/clutter-text.c | 113 +++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 32 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index c0d51543e..7d2a07751 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -61,6 +61,9 @@ #define DEFAULT_FONT_NAME "Sans 10" +/* cursor width in pixels */ +#define DEFAULT_CURSOR_SIZE 2 + /* We need at least three cached layouts to run the allocation without * regenerating a new layout. First the layout will be generated at * full width to get the preferred width, then it will be generated at @@ -150,6 +153,7 @@ struct _ClutterTextPrivate /* Where to draw the cursor */ ClutterGeometry cursor_pos; ClutterColor cursor_color; + gint cursor_size; gint max_length; @@ -438,17 +442,30 @@ clutter_text_coords_to_position (ClutterText *text, py = y * PANGO_SCALE; pango_layout_xy_to_index (clutter_text_get_layout (text), - px, py, &index_, &trailing); + px, py, + &index_, &trailing); return index_ + trailing; } +/* + * clutter_text_position_to_coords: + * @self: a #ClutterText + * @position: position in characters + * @x: return location for the X coordinate, or %NULL + * @y: return location for the Y coordinate, or %NULL + * @line_height: return location for the line height, or %NULL + * + * Retrieves the coordinates of the given @position. + * + * Return value: %TRUE if the conversion was successful + */ static gboolean clutter_text_position_to_coords (ClutterText *self, gint position, - gint *x, - gint *y, - gint *cursor_height) + ClutterUnit *x, + ClutterUnit *y, + ClutterUnit *line_height) { ClutterTextPrivate *priv = self->priv; PangoRectangle rect; @@ -463,10 +480,14 @@ clutter_text_position_to_coords (ClutterText *self, if (position == -1) { if (priv->text_visible) - index_ = strlen (priv->text); + index_ = priv->n_bytes; else index_ = priv->n_chars * priv_char_bytes; } + else if (position == 0) + { + index_ = 0; + } else { if (priv->text_visible) @@ -479,31 +500,33 @@ clutter_text_position_to_coords (ClutterText *self, &rect, NULL); if (x) - *x = rect.x / PANGO_SCALE; + *x = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.x); if (y) - *y = (rect.y + rect.height) / PANGO_SCALE; + *y = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.y); - if (cursor_height) - *cursor_height = rect.height / PANGO_SCALE; + if (line_height) + *line_height = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.height); - return TRUE; /* FIXME: should return false if coords were outside text */ + /* FIXME: should return false if coords were outside text */ + return TRUE; } static inline void clutter_text_ensure_cursor_position (ClutterText *self) { ClutterTextPrivate *priv = self->priv; - gint x, y, cursor_height; + ClutterUnit x, y, cursor_height; + x = y = cursor_height = 0; clutter_text_position_to_coords (self, priv->position, &x, &y, &cursor_height); - priv->cursor_pos.x = x; - priv->cursor_pos.y = y - cursor_height; - priv->cursor_pos.width = 2; - priv->cursor_pos.height = cursor_height; + priv->cursor_pos.x = CLUTTER_UNITS_TO_DEVICE (x); + priv->cursor_pos.y = CLUTTER_UNITS_TO_DEVICE (y); + priv->cursor_pos.width = priv->cursor_size; + priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height); g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } @@ -799,13 +822,12 @@ cursor_paint (ClutterText *self) for (line_no = 0; line_no < lines; line_no++) { PangoLayoutLine *line; - gint n_ranges; + gint n_ranges; gint *ranges; - gint i; - gint y; - gint height; + gint i; gint index; gint maxindex; + ClutterUnit y, height; line = pango_layout_get_line_readonly (layout, line_no); pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL); @@ -822,11 +844,20 @@ cursor_paint (ClutterText *self) NULL, &y, &height); for (i = 0; i < n_ranges; i++) - cogl_rectangle (ranges[i * 2 + 0] / PANGO_SCALE, - y - height, - ((ranges[i * 2 + 1] - ranges[i * 2 + 0]) - / PANGO_SCALE), - height); + { + gint range_x; + gint range_width; + + range_x = ranges[i * 2] + / PANGO_SCALE; + range_width = (ranges[i * 2 + 1] - ranges[i * 2]) + / PANGO_SCALE; + + cogl_rectangle (range_x, + CLUTTER_UNITS_TO_DEVICE (y), + range_width, + CLUTTER_UNITS_TO_DEVICE (height)); + } g_free (ranges); @@ -886,8 +917,9 @@ clutter_text_motion (ClutterActor *actor, clutter_actor_transform_stage_point (actor, x, y, &x, &y); - index_ = clutter_text_coords_to_position (ttext, CLUTTER_UNITS_TO_INT (x), - CLUTTER_UNITS_TO_INT (y)); + index_ = clutter_text_coords_to_position (ttext, + CLUTTER_UNITS_TO_INT (x), + CLUTTER_UNITS_TO_INT (y)); if (priv->selectable) clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_)); @@ -1023,14 +1055,21 @@ clutter_text_get_preferred_width (ClutterActor *self, ClutterTextPrivate *priv = text->priv; PangoRectangle logical_rect = { 0, }; PangoLayout *layout; + gint logical_width; ClutterUnit layout_width; layout = clutter_text_create_layout (text, -1); pango_layout_get_extents (layout, NULL, &logical_rect); - layout_width = logical_rect.width > 0 - ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.width) + /* the X coordinate of the logical rectangle might be non-zero + * according to the Pango documentation; hence, we need to offset + * the width accordingly + */ + logical_width = logical_rect.x + logical_rect.width; + + layout_width = logical_width > 0 + ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_width) : 1; if (min_width_p) @@ -1065,18 +1104,26 @@ clutter_text_get_preferred_height (ClutterActor *self, { PangoLayout *layout; PangoRectangle logical_rect = { 0, }; - ClutterUnit height; + gint logical_height; + ClutterUnit layout_height; layout = clutter_text_create_layout (text, for_width); pango_layout_get_extents (layout, NULL, &logical_rect); - height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_rect.height); + + /* the Y coordinate of the logical rectangle might be non-zero + * according to the Pango documentation; hence, we need to offset + * the height accordingly + */ + logical_height = logical_rect.y + logical_rect.height; + + layout_height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_height); if (min_height_p) - *min_height_p = height; + *min_height_p = layout_height; if (natural_height_p) - *natural_height_p = height; + *natural_height_p = layout_height; } } @@ -1881,6 +1928,8 @@ clutter_text_init (ClutterText *self) priv->priv_char = '*'; priv->max_length = 0; + + priv->cursor_size = DEFAULT_CURSOR_SIZE; } ClutterActor * From be64cbcdc22952ce2fa9f902e153e3bd9f4a08c9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 15 Dec 2008 18:46:08 +0000 Subject: [PATCH 054/108] Fix up/down key navigation The behaviour of ClutterText around the initial position is still a little bit erratic. This commit fixes the key navigation with Up and Down arrows around the first line of text. --- clutter/clutter-text.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 7d2a07751..51c29f29f 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1209,20 +1209,24 @@ clutter_text_real_move_up (ClutterText *self, layout = clutter_text_get_layout (self); - pango_layout_index_to_line_x (layout, - offset_to_bytes (priv->text, priv->position), + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, 0, &line_no, &x); + line_no -= 1; + if (line_no < 0) + return FALSE; + if (priv->x_pos != -1) x = priv->x_pos; else priv->x_pos = x; - line_no -= 1; - if (line_no < 0) - return FALSE; - layout_line = pango_layout_get_line_readonly (layout, line_no); if (!layout_line) return FALSE; @@ -1256,8 +1260,12 @@ clutter_text_real_move_down (ClutterText *self, layout = clutter_text_get_layout (self); - pango_layout_index_to_line_x (layout, - offset_to_bytes (priv->text, priv->position), + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, 0, &line_no, &x); From dc49dab35074ca27fcccf015e8990270eeaa4185 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:15:57 +0000 Subject: [PATCH 055/108] Fix line start and line end key binding behaviour Another fix for the key navigation behaviour around the zeroeth glyph in the layout. This commit adds a fast path for for the zero index when the cursor position is set as zero, in case we are using the line-start or line-end key bindings, similarly to what we did in commit be64cbcdc22952ce2fa9f902e153e3bd9f4a08c9 for the move-up and move-down bindings. --- clutter/clutter-text.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 51c29f29f..4aab2eb95 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -211,11 +211,6 @@ offset_to_bytes (const gchar *text, if (pos < 0) return strlen (text); -#if 0 - if (pos < 1) - return pos; -#endif - c = g_utf8_next_char (text); j = 1; len = strlen (text); @@ -1307,8 +1302,12 @@ clutter_text_real_line_start (ClutterText *self, layout = clutter_text_get_layout (self); - pango_layout_index_to_line_x (layout, - offset_to_bytes (priv->text, priv->position), + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); + + pango_layout_index_to_line_x (layout, index_, 0, &line_no, NULL); @@ -1342,7 +1341,11 @@ clutter_text_real_line_end (ClutterText *self, gint position; layout = clutter_text_get_layout (self); - index_ = offset_to_bytes (priv->text, priv->position); + + if (priv->position == 0) + index_ = 0; + else + index_ = offset_to_bytes (priv->text, priv->position); pango_layout_index_to_line_x (layout, index_, 0, @@ -2145,6 +2148,7 @@ clutter_text_get_selection (ClutterText *text) if (end_index == start_index) return g_strdup (""); + if (end_index < start_index) { gint temp = start_index; From ffb500d3a617ef3f0cfab4912c4ed4ca8c71599a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:19:56 +0000 Subject: [PATCH 056/108] Remove unused page up/down key bindings Moving the text by a "page" depends on being able to define a "page size" in terms of lines of text. Since we don't define something similar to an Adjustment that allows us to handle this behaviour, we should defer the paging implementation to a higher level class based on ClutterText. --- clutter/clutter-text.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4aab2eb95..b5e91a427 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1449,24 +1449,6 @@ clutter_text_real_activate (ClutterText *self, return FALSE; } -static gboolean -clutter_text_real_page_up (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) -{ - return FALSE; -} - -static gboolean -clutter_text_real_page_down (ClutterText *self, - const gchar *action, - guint keyval, - ClutterModifierType modifiers) -{ - return FALSE; -} - static void clutter_text_add_move_binding (ClutterBindingPool *pool, const gchar *action, @@ -1851,23 +1833,6 @@ clutter_text_class_init (ClutterTextClass *klass) G_CALLBACK (clutter_text_real_line_end), NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-up", - CLUTTER_Page_Up, 0, - G_CALLBACK (clutter_text_real_page_up), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-up", - CLUTTER_KP_Page_Up, 0, - G_CALLBACK (clutter_text_real_page_up), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-down", - CLUTTER_Page_Down, 0, - G_CALLBACK (clutter_text_real_page_down), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "page-up", - CLUTTER_KP_Page_Down, 0, - G_CALLBACK (clutter_text_real_page_down), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_Delete, 0, G_CALLBACK (clutter_text_real_del_next), From ea5a6abd9d599104ae4de7c0aecb529276d0d004 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:37:45 +0000 Subject: [PATCH 057/108] Add :cursor-size property to ClutterText We can control the width of the cursor when painting by using a simple property. The magic -1 number passed to the setter method will reset the cursor size to the default one of 2px. The getter method will return an unsigned integer with the current size. --- clutter/clutter-text.c | 80 +++++++++++++++++++++++++++++++++++++++++- clutter/clutter-text.h | 3 ++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index b5e91a427..86c366ca0 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -153,7 +153,7 @@ struct _ClutterTextPrivate /* Where to draw the cursor */ ClutterGeometry cursor_pos; ClutterColor cursor_color; - gint cursor_size; + guint cursor_size; gint max_length; @@ -179,6 +179,7 @@ enum PROP_CURSOR_VISIBLE, PROP_CURSOR_COLOR, PROP_CURSOR_COLOR_SET, + PROP_CURSOR_SIZE, PROP_EDITABLE, PROP_SELECTABLE, PROP_ACTIVATABLE, @@ -623,6 +624,10 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_cursor_color (self, g_value_get_boxed (value)); break; + case PROP_CURSOR_SIZE: + clutter_text_set_cursor_size (self, g_value_get_int (value)); + break; + case PROP_EDITABLE: clutter_text_set_editable (self, g_value_get_boolean (value)); break; @@ -686,6 +691,10 @@ clutter_text_get_property (GObject *gobject, g_value_set_boolean (value, priv->cursor_color_set); break; + case PROP_CURSOR_SIZE: + g_value_set_int (value, priv->cursor_size); + break; + case PROP_POSITION: g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position)); break; @@ -1583,6 +1592,21 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec); + /** + * ClutterText:cursor-size: + * + * The size of the cursor, in pixels. If set to -1 the size used will + * be the default cursor size of 2 pixels. + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("cursor-size", + "Cursor Size", + "The width of the cursor, in pixels", + -1, G_MAXINT, DEFAULT_CURSOR_SIZE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CURSOR_SIZE, pspec); + /** * ClutterText:position: * @@ -2847,6 +2871,60 @@ clutter_text_set_cursor_position (ClutterText *self, clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } +/** + * clutter_text_set_cursor_size: + * @self: a #ClutterText + * @size: the size of the cursor, in pixels, or -1 to use the + * default value + * + * Sets the size of the cursor of a #ClutterText. The cursor + * will only be visible if the #ClutterText:cursor-visible property + * is set to %TRUE. + * + * Since: 1.0 + */ +void +clutter_text_set_cursor_size (ClutterText *self, + gint size) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->cursor_size != size) + { + if (size < 0) + size = DEFAULT_CURSOR_SIZE; + + priv->cursor_size = size; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "cursor-size"); + } +} + +/** + * clutter_text_get_cursor_size: + * @self: a #ClutterText + * + * Retrieves the size of the cursor of a #ClutterText actor. + * + * Return value: the size of the cursor, in pixels + * + * Since: 1.0 + */ +guint +clutter_text_get_cursor_size (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE); + + return self->priv->cursor_size; +} + /** * clutter_text_set_text_visible: * @self: a #ClutterText diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 4001181b5..6d861c895 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -168,6 +168,9 @@ void clutter_text_set_cursor_color (ClutterText *self const ClutterColor *color); void clutter_text_get_cursor_color (ClutterText *self, ClutterColor *color); +void clutter_text_set_cursor_size (ClutterText *self, + gint size); +guint clutter_text_get_cursor_size (ClutterText *self); void clutter_text_set_selectable (ClutterText *self, gboolean selectable); gboolean clutter_text_get_selectable (ClutterText *self); From 7ebb9ff6b7bc04d2a47a0d7202f7190ce41faa80 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:39:52 +0000 Subject: [PATCH 058/108] Allow selecting with line-start and line-end bindings Instead of installing the line-start and line-end key bindings using the bare ClutterBindingPool API, we can use the internal clutter_text_add_move_binding(), which automatically installs the same key binding with the Shift modifier mask. This allows selecting when pressing Shift+Home or Shift+End. The selection behaviour is still incorrect around the zeroeth position, with all the text after the first line being selected. --- clutter/clutter-text.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 86c366ca0..f99b22b17 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1458,7 +1458,7 @@ clutter_text_real_activate (ClutterText *self, return FALSE; } -static void +static inline void clutter_text_add_move_binding (ClutterBindingPool *pool, const gchar *action, guint key_val, @@ -1836,26 +1836,21 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_KP_Down, G_CALLBACK (clutter_text_real_move_down)); - clutter_binding_pool_install_action (binding_pool, "line-start", - CLUTTER_Home, 0, - G_CALLBACK (clutter_text_real_line_start), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-start", - CLUTTER_KP_Home, 0, - G_CALLBACK (clutter_text_real_line_start), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-start", - CLUTTER_Begin, 0, - G_CALLBACK (clutter_text_real_line_start), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-end", - CLUTTER_End, 0, - G_CALLBACK (clutter_text_real_line_end), - NULL, NULL); - clutter_binding_pool_install_action (binding_pool, "line-end", - CLUTTER_KP_End, 0, - G_CALLBACK (clutter_text_real_line_end), - NULL, NULL); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_Home, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_KP_Home, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-start", + CLUTTER_Begin, + G_CALLBACK (clutter_text_real_line_start)); + clutter_text_add_move_binding (binding_pool, "line-end", + CLUTTER_End, + G_CALLBACK (clutter_text_real_line_end)); + clutter_text_add_move_binding (binding_pool, "line-end", + CLUTTER_KP_End, + G_CALLBACK (clutter_text_real_line_end)); clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_Delete, 0, From 32896d9aee66700f03417f3f88e0bba6d18d2e5c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 10:43:05 +0000 Subject: [PATCH 059/108] [docs] Add :cursor-size property accessors Add the ClutterText:cursor-size property accessors to the known gtk-doc symbols for the ClutterText section. --- doc/reference/clutter/clutter-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 9fb7924dd..95ae63d3b 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1743,6 +1743,8 @@ clutter_text_set_cursor_position clutter_text_get_cursor_position clutter_text_set_cursor_visible clutter_text_get_cursor_visible +clutter_text_set_cursor_size +clutter_text_get_cursor_size CLUTTER_IS_TEXT From e3ba2ddf0a0cdace9530ad8227fdd3a6a94bf36f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 11:30:47 +0000 Subject: [PATCH 060/108] [docs] Annotate properties and fix signals docs Annotate the properties without gtk-doc description, to get the "Since" attribute. Also, fix the ::cursor-event and ::activate signals documentation. --- clutter/clutter-text.c | 94 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index f99b22b17..7d8badc67 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1513,6 +1513,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); + /** + * ClutterText:text: + * + * The text to render inside the actor. + * + * Since: 1.0 + */ pspec = g_param_spec_string ("text", "Text", "The text to render", @@ -1520,6 +1527,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); + /** + * ClutterText:color: + * + * The color used to render the text. + * + * Since: 1.0 + */ pspec = clutter_param_spec_color ("color", "Font Color", "Color of the font used by the text", @@ -1531,6 +1545,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:editable: * * Whether key events delivered to the actor causes editing. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("editable", "Editable", @@ -1542,7 +1558,10 @@ clutter_text_class_init (ClutterTextClass *klass) /** * ClutterText:selectable: * - * Whether it is possible to select text. + * Whether it is possible to select text, either using the pointer + * or the keyboard. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("selectable", "Selectable", @@ -1555,6 +1574,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:activatable: * * Toggles whether return invokes the activate signal or not. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("activatable", "Activatable", @@ -1568,8 +1589,10 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:cursor-visible: * * Whether the input cursor is visible or not, it will only be visible - * if both cursor-visible is set and editable is set at the same time, - * the value defaults to TRUE. + * if both #ClutterText:cursor-visible and #ClutterText:editable are + * set to %TRUE. + * + * Since: 1.0 */ pspec = g_param_spec_boolean ("cursor-visible", "Cursor Visible", @@ -1578,6 +1601,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); + /** + * ClutterText:cursor-color: + * + * The color of the cursor. + * + * Since: 1.0 + */ pspec = clutter_param_spec_color ("cursor-color", "Cursor Color", "Cursor Color", @@ -1585,6 +1615,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); + /** + * ClutterText:cursor-color-set: + * + * Will be set to %TRUE if #ClutterText:cursor-color has been set. + * + * Since: 1.0 + */ pspec = g_param_spec_boolean ("cursor-color-set", "Cursor Color Set", "Whether the cursor color has been set", @@ -1611,6 +1648,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:position: * * The current input cursor position. -1 is taken to be the end of the text + * + * Since: 1.0 */ pspec = g_param_spec_int ("position", "Position", @@ -1624,6 +1663,8 @@ clutter_text_class_init (ClutterTextClass *klass) * ClutterText:selection-bound: * * The current input cursor position. -1 is taken to be the end of the text + * + * Since: 1.0 */ pspec = g_param_spec_int ("selection-bound", "Selection-bound", @@ -1634,6 +1675,14 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); + /** + * ClutterText:attributes: + * + * A list of #PangoStyleAttributes to be applied to the + * contents of the #ClutterText actor. + * + * Since: 1.0 + */ pspec = g_param_spec_boxed ("attributes", "Attributes", "A list of style attributes to apply to " @@ -1691,6 +1740,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec); + /** + * ClutterText:ellipsize: + * + * The preferred place to ellipsize the contents of the #ClutterText actor + * + * Since: 1.0 + */ pspec = g_param_spec_enum ("ellipsize", "Ellipsize", "The preferred place to ellipsize the string", @@ -1731,6 +1787,15 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); + /** + * ClutterText:text-visible: + * + * Whether the contents of the #ClutterText actor should be visible + * or substituted by a Unicode character, like in password text fields. + * The Unicode character is set using #ClutterText:invisible-char. + * + * Since: 1.0 + */ pspec = g_param_spec_boolean ("text-visible", "Text Visible", "Whether the text should be visible " @@ -1740,6 +1805,14 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT_VISIBLE, pspec); + /** + * ClutterText:invisible-char: + * + * The Unicode character used to render the contents of the #ClutterText + * actor if #ClutterText:text-invisible is set to %TRUE. + * + * Since: 1.0 + */ pspec = g_param_spec_unichar ("invisible-char", "Invisible Character", "The Unicode character used when the " @@ -1748,6 +1821,13 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + /** + * ClutterText:max-length: + * + * The maximum length of the contents of the #ClutterText actor. + * + * Since: 1.0 + */ pspec = g_param_spec_int ("max-length", "Max Length", "Maximum length of the text inside the actor", @@ -1777,7 +1857,10 @@ clutter_text_class_init (ClutterTextClass *klass) * @self: the #ClutterText that emitted the signal * @geometry: the coordinates of the cursor * - * FIXME + * The ::cursor-event signal is emitted whenever the cursor position + * changes inside a #ClutterText actor. Inside @geometry it is stored + * the current position and size of the cursor, relative to the actor + * itself. * * Since: 1.0 */ @@ -1796,7 +1879,8 @@ clutter_text_class_init (ClutterTextClass *klass) * @self: the #ClutterText that emitted the signal * * The ::activate signal is emitted each time the actor is 'activated' - * by the user, normally by pressing the 'Enter' key. + * by the user, normally by pressing the 'Enter' key. The signal is + * emitted only if #ClutterText:activatable is set to %TRUE. * * Since: 1.0 */ From 87f38475aed1827de5e48e0db0d5bff4ea1bcef5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 11:52:57 +0000 Subject: [PATCH 061/108] [docs] Add more gtk-doc annotations for ClutterText Document the ClutterText constructors and the :editable, :selectable and :activatable properties accessors. --- clutter/clutter-text.c | 116 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 7d8badc67..dd93c5b64 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2011,12 +2011,40 @@ clutter_text_init (ClutterText *self) priv->cursor_size = DEFAULT_CURSOR_SIZE; } +/** + * clutter_text_new: + * + * Creates a new #ClutterText actor. This actor can be used to + * display and edit text. + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ ClutterActor * clutter_text_new (void) { return g_object_new (CLUTTER_TYPE_TEXT, NULL); } +/** + * clutter_text_new_full: + * @font_name: a string with a font description + * @text: the contents of the actor + * @color: the color to be used to render @text + * + * Creates a new #ClutterText actor, using @font_name as the font + * description; @text will be used to set the contents of the actor; + * and @color will be used as the color to render @text. + * + * This function is equivalent to calling clutter_text_new(), + * clutter_text_set_font_name(), clutter_text_set_text() and + * clutter_text_set_color(). + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, @@ -2029,6 +2057,21 @@ clutter_text_new_full (const gchar *font_name, NULL); } +/** + * clutter_text_new_with_text: + * @font_name: a string with a font description + * @text: the contents of the actor + * + * Creates a new #ClutterText actor, using @font_name as the font + * description; @text will be used to set the contents of the actor. + * + * This function is equivalent to calling clutter_text_new(), + * clutter_text_set_font_name(), and clutter_text_set_text(). + * + * Return value: the newly created #ClutterText actor + * + * Since: 1.0 + */ ClutterActor * clutter_text_new_with_text (const gchar *font_name, const gchar *text) @@ -2039,6 +2082,19 @@ clutter_text_new_with_text (const gchar *font_name, NULL); } +/** + * clutter_text_set_editable: + * @self: a #ClutterText + * @editable: whether the #ClutterText should be editable + * + * Sets whether the #ClutterText actor should be editable. + * + * An editable #ClutterText with key focus set using + * clutter_actor_grab_key_focus() or clutter_stage_take_key_focus() + * will receive key events and will update its contents accordingly. + * + * Since: 1.0 + */ void clutter_text_set_editable (ClutterText *self, gboolean editable) @@ -2060,6 +2116,16 @@ clutter_text_set_editable (ClutterText *self, } } +/** + * clutter_text_get_editable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is editable or not. + * + * Return value: %TRUE if the actor is editable + * + * Since: 1.0 + */ gboolean clutter_text_get_editable (ClutterText *self) { @@ -2068,7 +2134,18 @@ clutter_text_get_editable (ClutterText *self) return self->priv->editable; } - +/** + * clutter_text_set_selectable: + * @self: a #ClutterText + * @selectable: whether the #ClutterText actor should be selectable + * + * Sets whether a #ClutterText actor should be selectable. + * + * A selectable #ClutterText will allow selecting its contents using + * the pointer or the keyboard. + * + * Since: 1.0 + */ void clutter_text_set_selectable (ClutterText *self, gboolean selectable) @@ -2090,6 +2167,16 @@ clutter_text_set_selectable (ClutterText *self, } } +/** + * clutter_text_get_selectable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is selectable or not. + * + * Return value: %TRUE if the actor is selectable + * + * Since: 1.0 + */ gboolean clutter_text_get_selectable (ClutterText *self) { @@ -2098,7 +2185,22 @@ clutter_text_get_selectable (ClutterText *self) return self->priv->selectable; } - +/** + * clutter_text_set_activatable: + * @self: a #ClutterText + * @activatable: whether the #ClutterText actor should be activatable + * + * Sets whether a #ClutterText actor should be activatable. + * + * An activatable #ClutterText actor will emit the #ClutterText::activate + * signal whenever the 'Enter' (or 'Return') key is pressed; if it is not + * activatable, a new line will be appended to the current content. + * + * An activatable #ClutterText must also be set as editable using + * clutter_text_set_editable(). + * + * Since: 1.0 + */ void clutter_text_set_activatable (ClutterText *self, gboolean activatable) @@ -2120,6 +2222,16 @@ clutter_text_set_activatable (ClutterText *self, } } +/** + * clutter_text_get_activatable: + * @self: a #ClutterText + * + * Retrieves whether a #ClutterText is activatable or not. + * + * Return value: %TRUE if the actor is activatable + * + * Since: 1.0 + */ gboolean clutter_text_get_activatable (ClutterText *self) { From 93d96dca52b21c63775a230f872de28fe0e0e3ea Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:02:58 +0000 Subject: [PATCH 062/108] Add ClutterText::activate() The clutter_text_activate() function will emit the ::activate signal if the :activatable property is set. This function is useful for subclasses or application code, for example if we are going to use ::captured-event or ::key-press-event signal handlers to intercept the Return key and emit the ::activate signal ourselves. --- clutter/clutter-text.c | 35 +++++++++++++++++++++++++++++++++++ clutter/clutter-text.h | 1 + 2 files changed, 36 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index dd93c5b64..22198a69f 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2240,6 +2240,41 @@ clutter_text_get_activatable (ClutterText *self) return self->priv->activatable; } +/** + * clutter_text_activate: + * @self: a #ClutterText + * + * Emits the #ClutterText::activate signal, if @self has been set + * as activatable using clutter_text_set_activatable(). + * + * This function can be used to emit the ::activate signal inside + * a #ClutterActor::captured-event or #ClutterActor::key-press-event + * signal handlers before the default signal handler for the + * #ClutterText is invoked. + * + * Return value: %TRUE if the ::activate signal has been emitted, + * and %FALSE otherwise + * + * Since: 1.0 + */ +gboolean +clutter_text_activate (ClutterText *self) +{ + ClutterTextPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + priv = self->priv; + + if (priv->activatable) + { + g_signal_emit (self, text_signals[ACTIVATE], 0); + return TRUE; + } + + return FALSE; +} + void clutter_text_set_cursor_visible (ClutterText *self, gboolean cursor_visible) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 6d861c895..326947955 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -187,6 +187,7 @@ gunichar clutter_text_get_invisible_char (ClutterText *self void clutter_text_set_max_length (ClutterText *self, gint max); gint clutter_text_get_max_length (ClutterText *self); +gboolean clutter_text_activate (ClutterText *self); G_END_DECLS From 4a43b582ac0de60464fbf0dfbebb26e549d452f6 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:05:10 +0000 Subject: [PATCH 063/108] Use clutter_text_activate() Instead of repeating the same code for the ::activate signal emission, use the clutter_text_activate() function inside the 'activate' key binding handler. --- clutter/clutter-text.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 22198a69f..cfeb3a796 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1446,16 +1446,7 @@ clutter_text_real_activate (ClutterText *self, guint keyval, ClutterModifierType modifiers) { - ClutterTextPrivate *priv = self->priv; - - if (priv->activatable) - { - g_signal_emit (self, text_signals[ACTIVATE], 0); - - return TRUE; - } - - return FALSE; + return clutter_text_activate (self); } static inline void From e28fb9ae103038e769553b488d1c084426edb966 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:25:15 +0000 Subject: [PATCH 064/108] [docs] Add more gtk-doc annotations Almost reaching full documentation. --- clutter/clutter-text.c | 50 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index cfeb3a796..f6c354ef6 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2266,6 +2266,24 @@ clutter_text_activate (ClutterText *self) return FALSE; } +/** + * clutter_text_set_cursor_visible: + * @self: a #ClutterText + * @cursor_visible: whether the cursor should be visible + * + * Sets whether the cursor of a #ClutterText actor should be + * visible or not. + * + * The color of the cursor will be the same as the text color + * unless clutter_text_set_cursor_color() has been called. + * + * The size of the cursor can be set using clutter_text_set_cursor_size(). + * + * The position of the cursor can be changed programmatically using + * clutter_text_set_cursor_position(). + * + * Since: 1.0 + */ void clutter_text_set_cursor_visible (ClutterText *self, gboolean cursor_visible) @@ -2287,6 +2305,16 @@ clutter_text_set_cursor_visible (ClutterText *self, } } +/** + * clutter_text_get_cursor_visible: + * @self: a #ClutterText + * + * Retrieves whether the cursor of a #ClutterText actor is visible. + * + * Return value: %TRUE if the cursor is visible + * + * Since: 1.0 + */ gboolean clutter_text_get_cursor_visible (ClutterText *self) { @@ -2295,6 +2323,18 @@ clutter_text_get_cursor_visible (ClutterText *self) return self->priv->cursor_visible; } +/** + * clutter_text_set_cursor_color: + * @self: a #ClutterText + * @color: the color of the cursor, or %NULL to unset it + * + * Sets the color of the cursor of a #ClutterText actor. + * + * If @color is %NULL, the cursor color will be the same as the + * text color. + * + * Since: 1.0 + */ void clutter_text_set_cursor_color (ClutterText *self, const ClutterColor *color) @@ -2320,7 +2360,15 @@ clutter_text_set_cursor_color (ClutterText *self, g_object_notify (G_OBJECT (self), "cursor-color-set"); } - +/** + * clutter_text_get_cursor_color: + * @self: a #ClutterText + * @color: return location for a #ClutterColor + * + * Retrieves the color of the cursor of a #ClutterText actor. + * + * Since: 1.0 + */ void clutter_text_get_cursor_color (ClutterText *self, ClutterColor *color) From 2209e174328790b140bb4a43c53fcdf7436d4a75 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:25:45 +0000 Subject: [PATCH 065/108] Fix ClutterText::get_selection() The clutter_text_get_selection() function was not checking the passed argument, and was still accessing the contents of the Text actor using clutter_text_get_text(). This commit also adds the last few gtk-doc annotations missing from ClutterText. --- clutter/clutter-text.c | 59 +++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index f6c354ef6..d9c813790 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2383,19 +2383,30 @@ clutter_text_get_cursor_color (ClutterText *self, *color = priv->cursor_color; } +/** + * clutter_text_get_selection: + * @self: a #ClutterText + * + * Retrieves the currently selected text. + * + * Return value: a newly allocated string containing the currently + * selected text, or %NULL. Use g_free() to free the returned + * string. + * + * Since: 1.0 + */ gchar * -clutter_text_get_selection (ClutterText *text) +clutter_text_get_selection (ClutterText *self) { ClutterTextPrivate *priv; - const gchar *utf8 = clutter_text_get_text (text); - gchar *str; - gint len; - gint start_index; - gint end_index; - gint start_offset; - gint end_offset; + gchar *str; + gint len; + gint start_index, end_index; + gint start_offset, end_offset; - priv = text->priv; + g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); + + priv = self->priv; start_index = priv->position; end_index = priv->selection_bound; @@ -2410,15 +2421,28 @@ clutter_text_get_selection (ClutterText *text) end_index = temp; } - start_offset = offset_to_bytes (utf8, start_index); - end_offset = offset_to_bytes (utf8, end_index); + start_offset = offset_to_bytes (priv->text, start_index); + end_offset = offset_to_bytes (priv->text, end_index); len = end_offset - start_offset; str = g_malloc (len + 1); - g_utf8_strncpy (str, utf8 + start_offset, end_index-start_index); + g_utf8_strncpy (str, priv->text + start_offset, end_index - start_index); + return str; } +/** + * clutter_text_set_selection_bound: + * @self: a #ClutterText + * @selection_bound: the position of the end of the selection, in characters + * + * Sets the other end of the selection, starting from the current + * cursor position. + * + * If @selection_bound is -1, the selection unset. + * + * Since: 1.0 + */ void clutter_text_set_selection_bound (ClutterText *self, gint selection_bound) @@ -2440,6 +2464,17 @@ clutter_text_set_selection_bound (ClutterText *self, } } +/** + * clutter_text_get_selection_bound: + * @self: a #ClutterText + * + * Retrieves the other end of the selection of a #ClutterText actor, + * in characters from the current cursor position. + * + * Return value: the position of the other end of the selection + * + * Since: 1.0 + */ gint clutter_text_get_selection_bound (ClutterText *self) { From a3fbdb594905cec0f822150b48c56837bc23dd73 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:41:20 +0000 Subject: [PATCH 066/108] Add ClutterText::set_selection() The clutter_text_set_selection() function is a convenience method for setting the cursor position and the selection boundary to a given position in a single call, with sanity checks for the positions. --- clutter/clutter-text.c | 38 ++++++++++++++++++++++++++++++++++++++ clutter/clutter-text.h | 3 +++ 2 files changed, 41 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index d9c813790..4d70c2fc5 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -2383,6 +2383,44 @@ clutter_text_get_cursor_color (ClutterText *self, *color = priv->cursor_color; } +/** + * clutter_text_set_selection: + * @self: a #ClutterText + * @start_pos: start of the selection, in characters + * @end_pos: end of the selection, in characters + * + * Selects the region of text between @start_pos and @end_pos. + * + * This function changes the position of the cursor to match + * @start_pos and the selection bound to match @end_pos. + * + * Since: 1.0 + */ +void +clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (end_pos < 0) + end_pos = priv->n_chars; + + start_pos = MIN (priv->n_chars, start_pos); + end_pos = MIN (priv->n_chars, end_pos); + + g_object_freeze_notify (G_OBJECT (self)); + + clutter_text_set_cursor_position (self, start_pos); + clutter_text_set_selection_bound (self, end_pos); + + g_object_thaw_notify (G_OBJECT (self)); +} + /** * clutter_text_get_selection: * @self: a #ClutterText diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 326947955..bd63478ee 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -177,6 +177,9 @@ gboolean clutter_text_get_selectable (ClutterText *self void clutter_text_set_selection_bound (ClutterText *self, gint selection_bound); gint clutter_text_get_selection_bound (ClutterText *self); +void clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos); gchar * clutter_text_get_selection (ClutterText *self); void clutter_text_set_text_visible (ClutterText *self, gboolean visible); From d85a1cb5a1dfce2364ca604c9ad33855d48d73aa Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 12:42:44 +0000 Subject: [PATCH 067/108] [docs] Add clutter_text_set_selection to gtk-doc Insert the newly added symbol to the ClutterText section in the API reference. --- doc/reference/clutter/clutter-sections.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 95ae63d3b..fa094506a 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1721,6 +1721,7 @@ clutter_text_set_max_length clutter_text_get_max_length clutter_text_set_selectable clutter_text_get_selectable +clutter_text_set_selection clutter_text_get_selection clutter_text_set_selection_bound clutter_text_get_selection_bound From ba586b46b304bbe3fe17b12b057574b8b1e08e32 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 13:58:54 +0000 Subject: [PATCH 068/108] [tests] Make the layout cache fail at the right place The GTest report output allows the developer to know where exactly a test unit failure happened. The test-text-cache unit makes it very hard to pinpoint the exact failure location because it relies on the output to be printed out - which is not the case when the tests are run as part of a make check. This commit makes each sub-test inside the unit fail exactly where the check function fails, which makes it easier to know which sub-test did actually fail. --- tests/conform/test-text-cache.c | 38 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/conform/test-text-cache.c b/tests/conform/test-text-cache.c index 26687fc00..d3a2cf5ec 100644 --- a/tests/conform/test-text-cache.c +++ b/tests/conform/test-text-cache.c @@ -71,7 +71,17 @@ check_result (CallbackData *data, const char *note, if (memcmp (&test_extents, &data->label_extents, sizeof (PangoRectangle))) { if (g_test_verbose ()) - g_print ("extents are different, "); + g_print ("extents are different: expected: %d, %d, %d, %d " + "-> text: %d, %d, %d, %d\n", + test_extents.x / 1024, + test_extents.y / 1024, + test_extents.width / 1024, + test_extents.height / 1024, + data->label_extents.x / 1024, + data->label_extents.y / 1024, + data->label_extents.width / 1024, + data->label_extents.height / 1024); + fail = TRUE; } else @@ -121,27 +131,27 @@ do_tests (CallbackData *data) /* TEST 1: change the text */ clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 0"); pango_layout_set_text (data->test_layout, "Counter 0", -1); - check_result (data, "Change text", TRUE); + g_assert (check_result (data, "Change text", TRUE) == FALSE); /* TEST 2: change a single character */ clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 1"); pango_layout_set_text (data->test_layout, "Counter 1", -1); - check_result (data, "Change a single character", TRUE); + g_assert (check_result (data, "Change a single character", TRUE) == FALSE); /* TEST 3: move the label */ clutter_actor_set_position (data->label, 10, 0); - check_result (data, "Move the label", FALSE); + g_assert (check_result (data, "Move the label", FALSE) == FALSE); /* TEST 4: change the font */ clutter_text_set_font_name (CLUTTER_TEXT (data->label), "Serif 15"); fd = pango_font_description_from_string ("Serif 15"); pango_layout_set_font_description (data->test_layout, fd); pango_font_description_free (fd); - check_result (data, "Change the font", TRUE); + g_assert (check_result (data, "Change the font", TRUE) == FALSE); /* TEST 5: change the color */ clutter_text_set_color (CLUTTER_TEXT (data->label), &red); - check_result (data, "Change the color", FALSE); + g_assert (check_result (data, "Change the color", FALSE) == FALSE); /* TEST 6: change the attributes */ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); @@ -154,19 +164,19 @@ do_tests (CallbackData *data) pango_layout_set_attributes (data->test_layout, attr_list_copy); pango_attr_list_unref (attr_list_copy); pango_attr_list_unref (attr_list); - check_result (data, "Change the attributes", TRUE); + g_assert (check_result (data, "Change the attributes", TRUE) == FALSE); /* TEST 7: change the text again */ clutter_text_set_attributes (CLUTTER_TEXT (data->label), NULL); clutter_text_set_text (CLUTTER_TEXT (data->label), long_text); pango_layout_set_attributes (data->test_layout, NULL); pango_layout_set_text (data->test_layout, long_text, -1); - check_result (data, "Change the text again", TRUE); + g_assert (check_result (data, "Change the text again", TRUE) == FALSE); /* TEST 8: enable markup */ clutter_text_set_use_markup (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_markup (data->test_layout, long_text, -1); - check_result (data, "Enable markup", TRUE); + g_assert (check_result (data, "Enable markup", TRUE) == FALSE); /* This part can't be a test because Clutter won't restrict the width if wrapping and ellipsizing is disabled so the extents will @@ -181,7 +191,7 @@ do_tests (CallbackData *data) clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_END); - check_result (data, "Enable ellipsize", TRUE); + g_assert (check_result (data, "Enable ellipsize", TRUE) == FALSE); clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_NONE); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_NONE); @@ -190,13 +200,13 @@ do_tests (CallbackData *data) /* TEST 10: enable line wrap */ clutter_text_set_line_wrap (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_WORD); - check_result (data, "Enable line wrap", TRUE); + g_assert (check_result (data, "Enable line wrap", TRUE) == FALSE); /* TEST 11: change wrap mode */ clutter_text_set_line_wrap_mode (CLUTTER_TEXT (data->label), PANGO_WRAP_CHAR); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_CHAR); - check_result (data, "Change wrap mode", TRUE); + g_assert (check_result (data, "Change wrap mode", TRUE) == FALSE); /* TEST 12: enable justify */ clutter_text_set_justify (CLUTTER_TEXT (data->label), TRUE); @@ -205,12 +215,12 @@ do_tests (CallbackData *data) justification after setting the text but this fixes it. See http://bugzilla.gnome.org/show_bug.cgi?id=551865 */ pango_layout_context_changed (data->test_layout); - check_result (data, "Enable justify", TRUE); + g_assert (check_result (data, "Enable justify", TRUE) == FALSE); /* TEST 13: change alignment */ clutter_text_set_alignment (CLUTTER_TEXT (data->label), PANGO_ALIGN_RIGHT); pango_layout_set_alignment (data->test_layout, PANGO_ALIGN_RIGHT); - check_result (data, "Change alignment", TRUE); + g_assert (check_result (data, "Change alignment", TRUE) == FALSE); clutter_main_quit (); From 6b782ce4e720f07bd678ce613b81987fde13ff38 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 15:53:57 +0000 Subject: [PATCH 069/108] Fix the selection behaviour around the 0th glyph After fixing the cursor position issues around the initial glyph of the layout, the selection position needs fixing as well. The fix is similar: check if the position of the selection is 0 and provide a fast path by setting the offset to 0. --- clutter/clutter-text.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 4d70c2fc5..e9eb6e174 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -811,8 +811,15 @@ cursor_paint (ClutterText *self) gint end_index; gint line_no; - start_index = offset_to_bytes (utf8, priv->position); - end_index = offset_to_bytes (utf8, priv->selection_bound); + if (priv->position == 0) + start_index = 0; + else + start_index = offset_to_bytes (utf8, priv->position); + + if (priv->selection_bound == 0) + end_index = 0; + else + end_index = offset_to_bytes (utf8, priv->selection_bound); if (start_index > end_index) { From 4a934c363c559896c2dede5374cc33b63a7c01a7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 22 Dec 2008 13:24:52 +0000 Subject: [PATCH 070/108] Do not include cogl-pango.h multiple times The clutter-private.h header already includes cogl-pango.h with the correct inclusion path, because the main context stores a pointer to the font map. There is no need for clutter-text.c to include cogl-pango.h again since it already includes clutter-private.h. --- clutter/clutter-private.h | 3 ++- clutter/clutter-text.c | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 81e030adf..fb8aea93b 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -38,6 +38,8 @@ #include +#include "pango/cogl-pango.h" + #include "clutter-backend.h" #include "clutter-event.h" #include "clutter-feature.h" @@ -45,7 +47,6 @@ #include "clutter-stage-manager.h" #include "clutter-stage-window.h" #include "clutter-stage.h" -#include "pango/cogl-pango.h" G_BEGIN_DECLS diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index e9eb6e174..ab2e323d7 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -54,11 +54,9 @@ #include "clutter-enum-types.h" #include "clutter-keysyms.h" #include "clutter-main.h" -#include "clutter-private.h" +#include "clutter-private.h" /* includes pango/cogl-pango.h */ #include "clutter-units.h" -#include "cogl-pango.h" - #define DEFAULT_FONT_NAME "Sans 10" /* cursor width in pixels */ From b021f518c18eb142791238cb4ecde8b1ea3707c0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 22 Dec 2008 13:29:10 +0000 Subject: [PATCH 071/108] Use "" as the default value for the :text property This follows the convention of GtkLabel/GtkEntry in GTK+ and the old ClutterEntry. It makes it easier to use strlen/strcmp etc on the output, since we can assume that it is always a string. This commit also updates the test unit for ClutterText to verify that the clutter_text_get_text() function also returns an empty string when a ClutterText actor has been created. --- clutter/clutter-text.c | 12 +++++++++--- tests/conform/test-clutter-text.c | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index ab2e323d7..480ce65e7 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1519,7 +1519,7 @@ clutter_text_class_init (ClutterTextClass *klass) pspec = g_param_spec_string ("text", "Text", "The text to render", - NULL, + "", CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); @@ -1981,7 +1981,11 @@ clutter_text_init (ClutterText *self) for (i = 0; i < N_CACHED_LAYOUTS; i++) priv->cached_layouts[i].layout = NULL; - priv->text = NULL; + /* default to "" so that clutter_text_get_text() will + * return a valid string and we can safely call strlen() + * or strcmp() on it + */ + priv->text = g_strdup (""); priv->text_color = default_text_color; priv->cursor_color = default_cursor_color; @@ -2640,7 +2644,9 @@ clutter_text_get_text (ClutterText *self) * @self: a #ClutterText * @text: the text to set * - * Sets the contents of a #ClutterText actor. + * Sets the contents of a #ClutterText actor. The @text string + * must not be %NULL; to unset the current contents of the + * #ClutterText actor simply pass "" (an empty string). * * Since: 1.0 */ diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index b47002a40..20cdb538c 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -76,7 +76,8 @@ test_text_empty (TestConformSimpleFixture *fixture, { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); - g_assert (clutter_text_get_text (text) == NULL); + g_assert_cmpstr (clutter_text_get_text (text), ==, ""); + g_assert_cmpint (*clutter_text_get_text (text), ==, '\0'); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); clutter_actor_destroy (CLUTTER_ACTOR (text)); From b8b8b155c45e54e8c2bd4d7e161a28b55a5088e1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 13:55:51 +0000 Subject: [PATCH 072/108] Add signals for Backend options The ClutterBackend instance at the moment lacks the ability to notify runtime changes of the font options and the resolution. For this reason, this commit adds a ::resolution-changed and a ::font-changed signals to the Backend class. The ::resolution-changed signal is emitted when set_resolution() is called with a different DPI; ::font-changed is emitted when the cairo_font_options_t* changes from the default. --- clutter/clutter-backend.c | 36 ++++++++++++++++++++++++++++++++++-- clutter/clutter-backend.h | 4 ++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 9a599c16b..086f27494 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -44,6 +44,7 @@ #include "clutter-backend.h" #include "clutter-debug.h" #include "clutter-fixed.h" +#include "clutter-marshal.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); @@ -62,6 +63,16 @@ struct _ClutterBackendPrivate cairo_font_options_t *font_options; }; +enum +{ + RESOLUTION_CHANGED, + FONT_CHANGED, + + LAST_SIGNAL +}; + +static guint backend_signals[LAST_SIGNAL] = { 0, }; + static void clutter_backend_dispose (GObject *gobject) { @@ -89,6 +100,24 @@ clutter_backend_class_init (ClutterBackendClass *klass) gobject_class->dispose = clutter_backend_dispose; g_type_class_add_private (gobject_class, sizeof (ClutterBackendPrivate)); + + backend_signals[RESOLUTION_CHANGED] = + g_signal_new (I_("resolution-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterBackendClass, resolution_changed), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + backend_signals[FONT_CHANGED] = + g_signal_new (I_("font-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterBackendClass, font_changed), + NULL, NULL, + clutter_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -388,8 +417,9 @@ clutter_backend_set_resolution (ClutterBackend *backend, priv->resolution = fixed_dpi; if (CLUTTER_CONTEXT ()->font_map) - cogl_pango_font_map_set_resolution (CLUTTER_CONTEXT ()->font_map, - COGL_FIXED_TO_FLOAT (fixed_dpi)); + cogl_pango_font_map_set_resolution (CLUTTER_CONTEXT ()->font_map, dpi); + + g_signal_emit (backend, backend_signals[RESOLUTION_CHANGED], 0); } /** @@ -445,6 +475,8 @@ clutter_backend_set_font_options (ClutterBackend *backend, priv->font_options = cairo_font_options_copy (options); else priv->font_options = NULL; + + g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); } } diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 27f49c46f..0669d4faa 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -77,6 +77,10 @@ struct _ClutterBackendClass ClutterStage *stage); void (* ensure_context) (ClutterBackend *backend, ClutterStage *stage); + + /* signals */ + void (* resolution_changed) (ClutterBackend *backend); + void (* font_changed) (ClutterBackend *backend); }; GType clutter_backend_get_type (void) G_GNUC_CONST; From 78628edf2f6f02db3d283169d93ae2d21e63a1ec Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:06:55 +0000 Subject: [PATCH 073/108] Add a per-actor PangoContext Rendering text inside an actor is pretty much impossible without using internal API to create the various pieces like the PangoContext and the font map. Each actor should have the ability to create a PangoContext, which is the only object needed to generate layouts and change the various Pango settings. This commit adds a clutter_actor_get_pango_context() function that creates a PangoContext inside the ClutterActor private data and allows the creation of PangoLayouts when needed. If the actor already has a PangoContext, the same instance is returned. The PangoContext is created only on demand. --- clutter/clutter-actor.c | 25 +++++++++++++++++++++++-- clutter/clutter-actor.h | 4 ++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 6a30c1c11..992ffde0b 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -221,7 +221,9 @@ struct _ClutterActorPrivate /* cached allocation is invalid (request has changed, probably) */ guint needs_allocation : 1; - guint has_clip : 1; + guint show_on_set_parent : 1; + guint has_clip : 1; + ClutterUnit clip[4]; /* Rotation angles */ @@ -260,7 +262,7 @@ struct _ClutterActorPrivate ShaderData *shader_data; - gboolean show_on_set_parent; + PangoContext *pango_context; }; enum @@ -7589,3 +7591,22 @@ clutter_actor_grab_key_focus (ClutterActor *self) if (parent && CLUTTER_IS_STAGE (parent)) clutter_stage_set_key_focus (CLUTTER_STAGE (parent), self); } + +PangoContext * +clutter_actor_get_pango_context (ClutterActor *self) +{ + ClutterActorPrivate *priv; + ClutterMainContext *ctx; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); + + priv = self->priv; + + if (priv->pango_context) + return priv->pango_context; + + ctx = CLUTTER_CONTEXT (); + priv->pango_context = _clutter_context_create_pango_context (ctx); + + return priv->pango_context; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 1874e283b..9461e9fa3 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -31,6 +31,8 @@ /* clutter-actor.h */ #include +#include + #include #include #include @@ -563,6 +565,8 @@ gboolean clutter_actor_get_paint_visibility (ClutterActor *self); void clutter_actor_grab_key_focus (ClutterActor *self); +PangoContext *clutter_actor_get_pango_context (ClutterActor *self); + G_END_DECLS #endif /* _HAVE_CLUTTER_ACTOR_H */ From a865a5d4ead867072d67f1ff57ac5a70d8589159 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:11:27 +0000 Subject: [PATCH 074/108] Create the PangoContext of the Text actor Instead of creating a single, private PangoContext for every Text actor, use the newly added clutter_actor_get_pango_context() function. --- clutter/clutter-text.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 480ce65e7..a58c92bb3 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -75,9 +75,6 @@ typedef struct _LayoutCache LayoutCache; -/* Probably move into main */ -static PangoContext *_context = NULL; - static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; static const ClutterColor default_text_color = { 0, 0, 0, 255 }; @@ -248,9 +245,11 @@ clutter_text_create_layout_no_cache (ClutterText *text, ClutterUnit allocation_width) { ClutterTextPrivate *priv = text->priv; + PangoContext *context; PangoLayout *layout; - layout = pango_layout_new (_context); + context = clutter_actor_get_pango_context (CLUTTER_ACTOR (text)); + layout = pango_layout_new (context); pango_layout_set_font_description (layout, priv->font_desc); @@ -1965,9 +1964,6 @@ clutter_text_init (ClutterText *self) ClutterTextPrivate *priv; int i; - if (G_UNLIKELY (_context == NULL)) - _context = _clutter_context_create_pango_context (CLUTTER_CONTEXT ()); - self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); priv->alignment = PANGO_ALIGN_LEFT; From 61d47ee3016c487312ef1e731af03960c9d42b42 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:15:10 +0000 Subject: [PATCH 075/108] Do not leak the PangoContext We hold a reference on the PangoContext we use for an actor, so we should remove the reference inside the dispose implementation. --- clutter/clutter-actor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 992ffde0b..7b550e9e9 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -1937,6 +1937,12 @@ clutter_actor_dispose (GObject *object) destroy_shader_data (self); + if (priv->pango_context) + { + g_object_unref (priv->pango_context); + priv->pango_context = NULL; + } + g_signal_emit (self, actor_signals[DESTROY], 0); G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); From 213d8f0e4e165554ad2c172c2b18cb29dfce9b76 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:27:41 +0000 Subject: [PATCH 076/108] Store the PangoContext inside the main context The PangoContext should be stored once, and inside the main Clutter context. Each actor for which clutter_actor_get_pango_context() has been called will hold a reference on the Pango context as well. This makes it possible to update the text rendering for Clutter by using only public API. --- clutter/clutter-actor.c | 1 + clutter/clutter-main.c | 15 ++++++++++----- clutter/clutter-private.h | 7 ++++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 7b550e9e9..50ea17b36 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7613,6 +7613,7 @@ clutter_actor_get_pango_context (ClutterActor *self) ctx = CLUTTER_CONTEXT (); priv->pango_context = _clutter_context_create_pango_context (ctx); + g_object_ref (priv->pango_context); return priv->pango_context; } diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 9edbce8c6..5b6a8d357 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -408,16 +408,21 @@ _clutter_context_create_pango_context (ClutterMainContext *self) gdouble resolution; cairo_font_options_t *font_options; + if (G_LIKELY (self->pango_context != NULL)) + context = self->pango_context; + else + { + context = cogl_pango_font_map_create_context (self->font_map); + self->pango_context = context; + } + + font_options = clutter_backend_get_font_options (self->backend); resolution = clutter_backend_get_resolution (self->backend); if (resolution < 0) resolution = 96.0; /* fall back */ - context = cogl_pango_font_map_create_context (self->font_map); - - pango_cairo_context_set_resolution (context, resolution); - - font_options = clutter_backend_get_font_options (self->backend); pango_cairo_context_set_font_options (context, font_options); + pango_cairo_context_set_resolution (context, resolution); return context; } diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index fb8aea93b..383b7df88 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -126,10 +126,11 @@ struct _ClutterMainContext gint fb_r_mask, fb_g_mask, fb_b_mask; gint fb_r_mask_used, fb_g_mask_used, fb_b_mask_used; - CoglPangoFontMap *font_map; /* Global font map */ + PangoContext *pango_context; /* Global Pango context */ + CoglPangoFontMap *font_map; /* Global font map */ - GSList *input_devices; /* For extra input devices, i.e - MultiTouch */ + GSList *input_devices; /* For extra input devices, i.e + MultiTouch */ }; #define CLUTTER_CONTEXT() (clutter_context_get_default ()) From 977bdcf89b449845f3b9aeb02d02fe1fb4577fcb Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 14:34:16 +0000 Subject: [PATCH 077/108] Update the PangoContext on backend changes When the ClutterBackend notifies of changes in the resolution or font options, update the PangoContext stored by Clutter's main context. This allows changing the backend font-related settings at runtime. --- clutter/clutter-main.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 5b6a8d357..50656101f 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -401,12 +401,27 @@ _clutter_do_pick (ClutterStage *stage, return clutter_get_actor_by_gid (id); } +static void +update_pango_context (ClutterBackend *backend, + ClutterMainContext *main_context) +{ + PangoContext *context = main_context->pango_context; + cairo_font_options_t *font_options; + gdouble resolution; + + font_options = clutter_backend_get_font_options (backend); + resolution = clutter_backend_get_resolution (backend); + if (resolution < 0) + resolution = 96.0; /* fall back */ + + pango_cairo_context_set_font_options (context, font_options); + pango_cairo_context_set_resolution (context, resolution); +} + PangoContext * _clutter_context_create_pango_context (ClutterMainContext *self) { PangoContext *context; - gdouble resolution; - cairo_font_options_t *font_options; if (G_LIKELY (self->pango_context != NULL)) context = self->pango_context; @@ -416,13 +431,14 @@ _clutter_context_create_pango_context (ClutterMainContext *self) self->pango_context = context; } - font_options = clutter_backend_get_font_options (self->backend); - resolution = clutter_backend_get_resolution (self->backend); - if (resolution < 0) - resolution = 96.0; /* fall back */ + g_signal_connect (self->backend, "resolution-changed", + G_CALLBACK (update_pango_context), + self); + g_signal_connect (self->backend, "font-changed", + G_CALLBACK (update_pango_context), + self); - pango_cairo_context_set_font_options (context, font_options); - pango_cairo_context_set_resolution (context, resolution); + update_pango_context (self->backend, self); return context; } From 72625421556c63a7f33afa262d4b60e29f48580b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 15:03:11 +0000 Subject: [PATCH 078/108] Store the default font name inside ClutterBackend The default backend stores some of the global defaults, like the font options, text resolution, double click settings. It should also store the default font name, to allow various text-based actors to share the same settings. When the font name changes, the ::font-changed signal is emitted, to allow actors to pick up the change. --- clutter/clutter-backend.c | 34 ++++++++++++++++++++++++++++++++++ clutter/clutter-backend.h | 5 +++++ 2 files changed, 39 insertions(+) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 086f27494..597558bde 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -49,6 +49,8 @@ G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); +#define DEFAULT_FONT_NAME "Sans 10" + #define CLUTTER_BACKEND_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BACKEND, ClutterBackendPrivate)) @@ -61,6 +63,8 @@ struct _ClutterBackendPrivate ClutterFixed resolution; cairo_font_options_t *font_options; + + gchar *font_name; }; enum @@ -76,6 +80,7 @@ static guint backend_signals[LAST_SIGNAL] = { 0, }; static void clutter_backend_dispose (GObject *gobject) { + ClutterBackendPrivate *priv = CLUTTER_BACKEND (gobject)->priv; ClutterMainContext *clutter_context; clutter_context = clutter_context_get_default (); @@ -87,6 +92,8 @@ clutter_backend_dispose (GObject *gobject) clutter_context->events_queue = NULL; } + g_free (priv->font_name); + clutter_backend_set_font_options (CLUTTER_BACKEND (gobject), NULL); G_OBJECT_CLASS (clutter_backend_parent_class)->dispose (gobject); @@ -514,3 +521,30 @@ clutter_backend_get_font_options (ClutterBackend *backend) return priv->font_options; } +void +clutter_backend_set_font_name (ClutterBackend *backend, + const gchar *font_name) +{ + ClutterBackendPrivate *priv; + + g_return_if_fail (CLUTTER_IS_BACKEND (backend)); + + priv = backend->priv; + + g_free (priv->font_name); + + if (font_name == NULL || *font_name == '\0') + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + else + priv->font_name = g_strdup (font_name); + + g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); +} + +G_CONST_RETURN gchar * +clutter_backend_get_font_name (ClutterBackend *backend) +{ + g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); + + return backend->priv->font_name; +} diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 0669d4faa..fda1d3413 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include @@ -99,6 +101,9 @@ guint clutter_backend_get_double_click_distance (ClutterBackend void clutter_backend_set_font_options (ClutterBackend *backend, cairo_font_options_t *options); cairo_font_options_t *clutter_backend_get_font_options (ClutterBackend *backend); +void clutter_backend_set_font_name (ClutterBackend *backend, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_backend_get_font_name (ClutterBackend *backend); G_END_DECLS From e3785f4f45d1ef8e0e65517c246cace4dade41ea Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 15:05:16 +0000 Subject: [PATCH 079/108] Use the default font from the Backend Instead of storing the default font name and size as a pre-processor macro, use the newly added ClutterBackend API to retrieve the current default font from the backend. --- clutter/clutter-text.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index a58c92bb3..b8b474076 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -57,8 +57,6 @@ #include "clutter-private.h" /* includes pango/cogl-pango.h */ #include "clutter-units.h" -#define DEFAULT_FONT_NAME "Sans 10" - /* cursor width in pixels */ #define DEFAULT_CURSOR_SIZE 2 @@ -1962,6 +1960,7 @@ static void clutter_text_init (ClutterText *self) { ClutterTextPrivate *priv; + const gchar *font_name; int i; self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self); @@ -1986,8 +1985,10 @@ clutter_text_init (ClutterText *self) priv->text_color = default_text_color; priv->cursor_color = default_cursor_color; - priv->font_name = g_strdup (DEFAULT_FONT_NAME); - priv->font_desc = pango_font_description_from_string (priv->font_name); + /* get the default font name from the context */ + font_name = clutter_backend_get_font_name (clutter_get_default_backend ()); + priv->font_name = g_strdup (font_name); + priv->font_desc = pango_font_description_from_string (font_name); priv->position = -1; priv->selection_bound = -1; @@ -2549,11 +2550,13 @@ clutter_text_get_font_name (ClutterText *text) /** * clutter_text_set_font_name: * @self: a #ClutterText - * @font_name: a font name + * @font_name: a font name, or %NULL to set the default font name * * Sets the font used by a #ClutterText. The @font_name string - * must be something that can be parsed by the - * pango_font_description_from_string() function, like: + * must either be %NULL, which means that the font name from the + * default #ClutterBackend will be used; or be something that can + * be parsed by the pango_font_description_from_string() function, + * like: * * |[ * clutter_text_set_font_name (text, "Sans 10pt"); @@ -2572,8 +2575,9 @@ clutter_text_set_font_name (ClutterText *self, g_return_if_fail (CLUTTER_IS_TEXT (self)); + /* get the default font name from the backend */ if (!font_name || font_name[0] == '\0') - font_name = DEFAULT_FONT_NAME; + font_name = clutter_backend_get_font_name (clutter_get_default_backend ()); priv = self->priv; From b4a3944b2b6bde5b57f86519135b959f4607cad3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 16:27:54 +0000 Subject: [PATCH 080/108] Return the default font name if no font name is set When calling clutter_backend_get_font_name(), if no default font name has previously been set, we just set the default and return a pointer to it - like we do for the font options. --- clutter/clutter-backend.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 597558bde..f49fa66da 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -544,7 +544,16 @@ clutter_backend_set_font_name (ClutterBackend *backend, G_CONST_RETURN gchar * clutter_backend_get_font_name (ClutterBackend *backend) { + ClutterBackendPrivate *priv; + g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); - return backend->priv->font_name; + priv = backend->priv; + + if (G_LIKELY (priv->font_name)) + return priv->font_name; + + priv->font_name = g_strdup (DEFAULT_FONT_NAME); + + return priv->font_name; } From 9389d9755d9121c1f99a5dc208ef90a10ca1b004 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 16:40:26 +0000 Subject: [PATCH 081/108] Add the ability to set the text direction The direction of the text depends on the locale, and it is the basic setting needed to enable internationalization of user interfaces. This commit allows setting the direction of the PangoContext instance used by Clutter by using the CLUTTER_TEXT_DIRECTION environment variable, or by passing the --clutter-text-direction command line argument. Valid values are: ltr - for left-to-right locales rtl - for right-to-left locales The default is LTR. Ideally, this should be a value set by the localization teams on the PO file, but this step requires some build system surgery to allow the translation of the Clutter strings. --- clutter/clutter-main.c | 68 +++++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 50656101f..a7ec4b923 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -53,21 +53,23 @@ #include "pango/cogl-pango.h" /* main context */ -static ClutterMainContext *ClutterCntx = NULL; +static ClutterMainContext *ClutterCntx = NULL; /* main lock and locking/unlocking functions */ -static GMutex *clutter_threads_mutex = NULL; -static GCallback clutter_threads_lock = NULL; -static GCallback clutter_threads_unlock = NULL; +static GMutex *clutter_threads_mutex = NULL; +static GCallback clutter_threads_lock = NULL; +static GCallback clutter_threads_unlock = NULL; -static gboolean clutter_is_initialized = FALSE; -static gboolean clutter_show_fps = FALSE; -static gboolean clutter_fatal_warnings = FALSE; +static gboolean clutter_is_initialized = FALSE; +static gboolean clutter_show_fps = FALSE; +static gboolean clutter_fatal_warnings = FALSE; -static guint clutter_default_fps = 60; +static guint clutter_default_fps = 60; -static guint clutter_main_loop_level = 0; -static GSList *main_loops = NULL; +static guint clutter_main_loop_level = 0; +static GSList *main_loops = NULL; + +static PangoDirection clutter_text_direction = PANGO_DIRECTION_LTR; guint clutter_debug_flags = 0; /* global clutter debug flag */ @@ -401,21 +403,51 @@ _clutter_do_pick (ClutterStage *stage, return clutter_get_actor_by_gid (id); } +static PangoDirection +clutter_get_text_direction (void) +{ + const gchar *direction; + PangoDirection dir = PANGO_DIRECTION_LTR; + + direction = g_getenv ("CLUTTER_TEXT_DIRECTION"); + if (direction && *direction != '\0') + { + if (strcmp (direction, "rtl") == 0) + dir = PANGO_DIRECTION_RTL; + else + dir = PANGO_DIRECTION_LTR; + } + else + dir = PANGO_DIRECTION_LTR; + + return dir; +} + static void update_pango_context (ClutterBackend *backend, ClutterMainContext *main_context) { PangoContext *context = main_context->pango_context; + PangoFontDescription *font_desc; cairo_font_options_t *font_options; + const gchar *font_name; gdouble resolution; + font_name = clutter_backend_get_font_name (backend); + font_desc = pango_font_description_from_string (font_name); + font_options = clutter_backend_get_font_options (backend); + resolution = clutter_backend_get_resolution (backend); if (resolution < 0) resolution = 96.0; /* fall back */ + pango_context_set_font_description (context, font_desc); + pango_context_set_base_dir (context, clutter_text_direction); pango_cairo_context_set_font_options (context, font_options); pango_cairo_context_set_resolution (context, resolution); + + pango_font_description_free (font_desc); } PangoContext * @@ -1025,6 +1057,17 @@ clutter_get_timestamp (void) #endif } +static gboolean +clutter_arg_direction_cb (const char *key, + const char *value, + gpointer user_data) +{ + clutter_text_direction = + (strcmp (value, "rtl") == 0) ? PANGO_DIRECTION_RTL + : PANGO_DIRECTION_LTR; + + return TRUE; +} #ifdef CLUTTER_ENABLE_DEBUG static gboolean @@ -1098,6 +1141,8 @@ clutter_init_real (GError **error) cogl_pango_font_map_set_resolution (ctx->font_map, resolution); cogl_pango_font_map_set_use_mipmapping (ctx->font_map, TRUE); + clutter_text_direction = clutter_get_text_direction (); + /* Stage will give us a GL Context etc */ stage = clutter_stage_get_default (); if (!stage) @@ -1168,6 +1213,9 @@ static GOptionEntry clutter_args[] = { "Default frame rate", "FPS" }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings, "Make all warnings fatal", NULL }, + { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK, + clutter_arg_direction_cb, + "Direction for the text", "DIRECTION" }, #ifdef CLUTTER_ENABLE_DEBUG { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb, "Clutter debugging flags to set", "FLAGS" }, From f777bc629ac90e2e0b70705823ecd3fb74f2cded Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 23 Dec 2008 16:59:14 +0000 Subject: [PATCH 082/108] [docs] Document the newly added functions Document the ClutterBackend font name accessors, and the clutter_actor_get_pango_context() getter method. --- clutter/clutter-actor.c | 21 +++++++++++++++++++ clutter/clutter-backend.c | 24 ++++++++++++++++++++++ doc/reference/clutter/clutter-sections.txt | 9 +++++--- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 50ea17b36..b4068b5d2 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7598,6 +7598,27 @@ clutter_actor_grab_key_focus (ClutterActor *self) clutter_stage_set_key_focus (CLUTTER_STAGE (parent), self); } +/** + * clutter_actor_get_pango_context: + * @self: a #ClutterActor + * + * Retrieves the #PangoContext for @self. The actor's #PangoContext + * is already configured using the appropriate font map, resolution + * and font options. + * + * The returned #PangoContext will be updated each time the options + * stored by the default #ClutterBackend change. + * + * You can use the returned #PangoContext to create a #PangoLayout + * and render text using cogl_pango_render_layout() to reuse the + * glyphs cache also used by Clutter. + * + * Return value: the #PangoContext for a #ClutterActor. The returned + * #PangoContext is owned by the actor and should not be unreferenced + * by the application code + * + * Since: 1.0 + */ PangoContext * clutter_actor_get_pango_context (ClutterActor *self) { diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index f49fa66da..4c60d40ff 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -521,6 +521,18 @@ clutter_backend_get_font_options (ClutterBackend *backend) return priv->font_options; } +/** + * clutter_backend_set_font_name: + * @backend: a #ClutterBackend + * @font_name: the name of the font + * + * Sets the default font to be used by Clutter. The @font_name string + * must either be %NULL, which means that the font name from the + * default #ClutterBackend will be used; or be something that can + * be parsed by the pango_font_description_from_string() function. + * + * Since: 1.0 + */ void clutter_backend_set_font_name (ClutterBackend *backend, const gchar *font_name) @@ -541,6 +553,18 @@ clutter_backend_set_font_name (ClutterBackend *backend, g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); } +/** + * clutter_backend_get_font_name: + * @backend: a #ClutterBackend + * + * Retrieves the default font name as set by + * clutter_backend_set_font_name(). + * + * Return value: the font name for the backend. The returned string is + * owned by the #ClutterBackend and should never be modified or freed + * + * Since: 1.0 + */ G_CONST_RETURN gchar * clutter_backend_get_font_name (ClutterBackend *backend) { diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index fa094506a..2bda1aeea 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -478,6 +478,7 @@ clutter_actor_get_rotationx clutter_actor_grab_key_focus +clutter_actor_get_pango_context CLUTTER_TYPE_GEOMETRY @@ -872,14 +873,16 @@ clutter_behaviour_ellipse_get_type clutter-backend ClutterBackend clutter_get_default_backend -clutter_backend_get_resolution clutter_backend_set_resolution -clutter_backend_get_double_click_time +clutter_backend_get_resolution clutter_backend_set_double_click_time -clutter_backend_get_double_click_distance +clutter_backend_get_double_click_time clutter_backend_set_double_click_distance +clutter_backend_get_double_click_distance clutter_backend_set_font_options clutter_backend_get_font_options +clutter_backend_set_font_name +clutter_backend_get_font_name CLUTTER_BACKEND CLUTTER_IS_BACKEND From 605ec10c3242f7024b77469ff38abf6a06ef08cc Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 17:40:41 +0000 Subject: [PATCH 083/108] [docs] Remove ClutterLabel and ClutterEntry sections Remove the sections for the Label and the Entry actors, which have been replaced by ClutterText. --- doc/reference/clutter/clutter-sections.txt | 86 ---------------------- 1 file changed, 86 deletions(-) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 2bda1aeea..6d3a339c0 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -66,48 +66,6 @@ clutter_unit_get_type clutter_param_unit_get_type -
-clutter-label -ClutterLabel -ClutterLabel -ClutterLabelClass -clutter_label_new -clutter_label_new_with_text -clutter_label_new_full -clutter_label_set_text -clutter_label_get_text -clutter_label_set_font_name -clutter_label_get_font_name -clutter_label_set_color -clutter_label_get_color -clutter_label_set_ellipsize -clutter_label_get_ellipsize -clutter_label_set_line_wrap -clutter_label_get_line_wrap -clutter_label_set_line_wrap_mode -clutter_label_get_line_wrap_mode -clutter_label_get_layout -clutter_label_set_attributes -clutter_label_get_attributes -clutter_label_set_use_markup -clutter_label_get_use_markup -clutter_label_set_alignment -clutter_label_get_alignment -clutter_label_get_justify -clutter_label_set_justify - - -CLUTTER_LABEL -CLUTTER_IS_LABEL -CLUTTER_TYPE_LABEL -CLUTTER_LABEL_CLASS -CLUTTER_IS_LABEL_CLASS -CLUTTER_LABEL_GET_CLASS - -ClutterLabelPrivate -clutter_label_get_type -
-
clutter-behaviour ClutterBehaviour @@ -1225,50 +1183,6 @@ CLUTTER_COGL CLUTTER_NO_FPU
-
-clutter-entry -ClutterEntry -ClutterEntry -ClutterEntryClass -clutter_entry_new -clutter_entry_new_with_text -clutter_entry_new_full -clutter_entry_set_text -clutter_entry_get_text -clutter_entry_set_font_name -clutter_entry_get_font_name -clutter_entry_set_color -clutter_entry_get_color -clutter_entry_get_layout -clutter_entry_set_alignment -clutter_entry_get_alignment -clutter_entry_set_cursor_position -clutter_entry_get_cursor_position -clutter_entry_handle_key_event -clutter_entry_insert_unichar -clutter_entry_delete_chars -clutter_entry_insert_text -clutter_entry_delete_text -clutter_entry_set_visible_cursor -clutter_entry_get_visible_cursor -clutter_entry_set_visibility -clutter_entry_get_visibility -clutter_entry_set_invisible_char -clutter_entry_get_invisible_char -clutter_entry_set_max_length -clutter_entry_get_max_length - -CLUTTER_ENTRY -CLUTTER_IS_ENTRY -CLUTTER_TYPE_ENTRY -CLUTTER_ENTRY_CLASS -CLUTTER_IS_ENTRY_CLASS -CLUTTER_ENTRY_GET_CLASS - -ClutterEntryPrivate -clutter_entry_get_type -
-
clutter-effect Clutter Effects From 92e8b886d2b21618277079306169a8aa31a03806 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 16 Dec 2008 17:44:13 +0000 Subject: [PATCH 084/108] [docs] Add clutter_text_activate() Add the last unused symbol to the ClutterText section. --- doc/reference/clutter/clutter-sections.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 6d3a339c0..33317aae2 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1664,6 +1664,9 @@ clutter_text_get_cursor_visible clutter_text_set_cursor_size clutter_text_get_cursor_size + +clutter_text_activate + CLUTTER_IS_TEXT CLUTTER_IS_TEXT_CLASS From 55a22d57507dbafbc99fdb01820d5a7f41404e36 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 12:27:45 +0000 Subject: [PATCH 085/108] [text] Add select-all binding Bind Ctrl+A to the "select all" action. --- clutter/clutter-text.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index b8b474076..329c389d1 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1379,6 +1379,18 @@ clutter_text_real_line_end (ClutterText *self, return TRUE; } +static gboolean +clutter_text_real_select_all (ClutterText *self, + const gchar *action, + guint keyval, + ClutterModifierType modifiers) +{ + clutter_text_set_cursor_position (self, 0); + clutter_text_set_selection_bound (self, self->priv->n_chars); + + return TRUE; +} + static gboolean clutter_text_real_del_next (ClutterText *self, const gchar *action, @@ -1929,6 +1941,11 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_KP_End, G_CALLBACK (clutter_text_real_line_end)); + clutter_binding_pool_install_action (binding_pool, "select-all", + CLUTTER_a, CLUTTER_CONTROL_MASK, + G_CALLBACK (clutter_text_real_select_all), + NULL, NULL); + clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_Delete, 0, G_CALLBACK (clutter_text_real_del_next), From e56fe478ef9ae30584480827805e0dd6a8834e0b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 12:49:01 +0000 Subject: [PATCH 086/108] Allow overriding actions inside a BindingPool As of now, a key binding installed into a BindingPool is always there and cannot be changed. This is problematic for sub-classes trying to override the callback or the action for a given key binding. This commit adds the ability to override the closure for an existing key binding inside a binding pool -- assumed the caller knows the key symbol and modifiers used to install the key binding in the first place. --- clutter/clutter-binding-pool.c | 125 +++++++++++++++++++++++++++++++++ clutter/clutter-binding-pool.h | 70 ++++++++++-------- 2 files changed, 165 insertions(+), 30 deletions(-) diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index 8b567b28d..de31870b3 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -476,6 +476,131 @@ clutter_binding_pool_install_closure (ClutterBindingPool *pool, g_hash_table_insert (pool->entries_hash, entry, entry); } +/** + * clutter_binding_pool_override_action: + * @pool: a #ClutterBindingPool + * @key_val: key symbol + * @modifiers: bitmask of modifiers + * @callback: function to be called when the action is activated + * @data: data to be passed to @callback + * @notify: function to be called when the action is removed + * from the pool + * + * Allows overriding the action for @key_val and @modifiers inside a + * #ClutterBindingPool. See clutter_binding_pool_install_action(). + * + * When an action has been activated using clutter_binding_pool_activate() + * the passed @callback will be invoked (with @data). + * + * Actions can be blocked with clutter_binding_pool_block_action() + * and then unblocked using clutter_binding_pool_unblock_action(). + * + * Since: 1.0 + */ +void +clutter_binding_pool_override_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify) +{ + ClutterBindingEntry *entry; + GClosure *closure; + + g_return_if_fail (pool != NULL); + g_return_if_fail (key_val != 0); + g_return_if_fail (callback != NULL); + + entry = binding_pool_lookup_entry (pool, key_val, modifiers); + if (G_UNLIKELY (entry == NULL)) + { + g_warning ("There is no action for the given key symbol " + "of %d (modifiers: %d) installed inside the " + "binding pool.", + key_val, modifiers); + return; + } + + if (entry->closure) + { + g_closure_unref (entry->closure); + entry->closure = NULL; + } + + closure = g_cclosure_new (callback, data, (GClosureNotify) notify); + entry->closure = g_closure_ref (closure); + g_closure_sink (closure); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + { + GClosureMarshal marshal; + + marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM; + g_closure_set_marshal (closure, marshal); + } +} + +/** + * clutter_binding_pool_override_action: + * @pool: a #ClutterBindingPool + * @key_val: key symbol + * @modifiers: bitmask of modifiers + * @closure: a #GClosure + * + * A #GClosure variant of clutter_binding_pool_override_action(). + * + * Allows overriding the action for @key_val and @modifiers inside a + * #ClutterBindingPool. See clutter_binding_pool_install_closure(). + * + * When an action has been activated using clutter_binding_pool_activate() + * the passed @callback will be invoked (with @data). + * + * Actions can be blocked with clutter_binding_pool_block_action() + * and then unblocked using clutter_binding_pool_unblock_action(). + * + * Since: 1.0 + */ +void +clutter_binding_pool_override_closure (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure) +{ + ClutterBindingEntry *entry; + + g_return_if_fail (pool != NULL); + g_return_if_fail (key_val != 0); + g_return_if_fail (closure != NULL); + + entry = binding_pool_lookup_entry (pool, key_val, modifiers); + if (G_UNLIKELY (entry == NULL)) + { + g_warning ("There is no action for the given key symbol " + "of %d (modifiers: %d) installed inside the " + "binding pool.", + key_val, modifiers); + return; + } + + if (entry->closure) + { + g_closure_unref (entry->closure); + entry->closure = NULL; + } + + entry->closure = g_closure_ref (closure); + g_closure_sink (closure); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + { + GClosureMarshal marshal; + + marshal = clutter_marshal_BOOLEAN__STRING_UINT_ENUM; + g_closure_set_marshal (closure, marshal); + } +} + gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool) { diff --git a/clutter/clutter-binding-pool.h b/clutter/clutter-binding-pool.h index 47692403e..6e2dac4bb 100644 --- a/clutter/clutter-binding-pool.h +++ b/clutter/clutter-binding-pool.h @@ -56,40 +56,50 @@ typedef gboolean (* ClutterBindingActionFunc) (GObject *gobject, guint key_val, ClutterModifierType modifiers); -ClutterBindingPool * clutter_binding_pool_new (const gchar *name); -ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass); -ClutterBindingPool * clutter_binding_pool_find (const gchar *name); +ClutterBindingPool * clutter_binding_pool_new (const gchar *name); +ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass); +ClutterBindingPool * clutter_binding_pool_find (const gchar *name); -void clutter_binding_pool_install_action (ClutterBindingPool *pool, - const gchar *action_name, - guint key_val, - ClutterModifierType modifiers, - GCallback callback, - gpointer data, - GDestroyNotify notify); -void clutter_binding_pool_install_closure (ClutterBindingPool *pool, - const gchar *action_name, - guint key_val, - ClutterModifierType modifiers, - GClosure *closure); +void clutter_binding_pool_install_action (ClutterBindingPool *pool, + const gchar *action_name, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify); +void clutter_binding_pool_install_closure (ClutterBindingPool *pool, + const gchar *action_name, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure); +void clutter_binding_pool_override_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GCallback callback, + gpointer data, + GDestroyNotify notify); +void clutter_binding_pool_override_closure (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GClosure *closure); -gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool); -G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers); -void clutter_binding_pool_remove_action (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers); +gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool); +G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers); +void clutter_binding_pool_remove_action (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers); -gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, - guint key_val, - ClutterModifierType modifiers, - GObject *gobject); +gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, + guint key_val, + ClutterModifierType modifiers, + GObject *gobject); -void clutter_binding_pool_block_action (ClutterBindingPool *pool, - const gchar *action_name); -void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, - const gchar *action_name); +void clutter_binding_pool_block_action (ClutterBindingPool *pool, + const gchar *action_name); +void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, + const gchar *action_name); G_END_DECLS From fa431f64923d233cea61aac9f1df75a43e323158 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 12:52:29 +0000 Subject: [PATCH 087/108] [docs] Add the new API to the BindingPool section Add the clutter_binding_pool_override_* family of functions to the BindingPool section of the Clutter API reference. --- doc/reference/clutter/clutter-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 33317aae2..7883e2e9c 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1591,6 +1591,8 @@ clutter_binding_pool_find clutter_binding_pool_install_action clutter_binding_pool_install_closure +clutter_binding_pool_override_action +clutter_binding_pool_override_closure clutter_binding_pool_list_actions clutter_binding_pool_find_action clutter_binding_pool_remove_action From 33459f63d61c9cf984378ff395a6dffa4ae87d46 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 15:26:57 +0000 Subject: [PATCH 088/108] Whitespace fixes --- clutter/clutter-text.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 329c389d1..e90491722 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -38,8 +38,7 @@ * #ClutterText is available since Clutter 1.0 */ -/* TODO: undo/redo hooks? - */ +/* TODO: undo/redo hooks? */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -67,7 +66,7 @@ * be regenerated at a different width to get the height for the * actual allocated width */ -#define N_CACHED_LAYOUTS 3 +#define N_CACHED_LAYOUTS 3 #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate)) @@ -866,7 +865,6 @@ cursor_paint (ClutterText *self) } g_free (ranges); - } } } From d622a4dd8933b33f9328beaf1bf825451cadd025 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 15:27:33 +0000 Subject: [PATCH 089/108] Rename the PangoContext creation functions The _clutter_context_create_pango_context() should create a new context; the function returning the PangoContext stored inside the MainContext structure should be named _get_pango_context() instead. --- clutter/clutter-main.c | 50 +++++++++++++++++++++++---------------- clutter/clutter-private.h | 1 + 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index a7ec4b923..588b00a84 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -424,21 +424,20 @@ clutter_get_text_direction (void) } static void -update_pango_context (ClutterBackend *backend, - ClutterMainContext *main_context) +update_pango_context (ClutterBackend *backend, + PangoContext *context) { - PangoContext *context = main_context->pango_context; PangoFontDescription *font_desc; cairo_font_options_t *font_options; const gchar *font_name; gdouble resolution; font_name = clutter_backend_get_font_name (backend); + font_options = clutter_backend_get_font_options (backend); + resolution = clutter_backend_get_resolution (backend); + font_desc = pango_font_description_from_string (font_name); - font_options = clutter_backend_get_font_options (backend); - - resolution = clutter_backend_get_resolution (backend); if (resolution < 0) resolution = 96.0; /* fall back */ @@ -450,26 +449,35 @@ update_pango_context (ClutterBackend *backend, pango_font_description_free (font_desc); } +PangoContext * +_clutter_context_get_pango_context (ClutterMainContext *self) +{ + if (G_LIKELY (self->pango_context)) + { + PangoContext *context; + + context = cogl_pango_font_map_create_context (self->font_map); + self->pango_context = context; + + g_signal_connect (self->backend, "resolution-changed", + G_CALLBACK (update_pango_context), + self->pango_context); + g_signal_connect (self->backend, "font-changed", + G_CALLBACK (update_pango_context), + self->pango_context); + } + + update_pango_context (self->backend, self->pango_context); + + return self->pango_context; +} + PangoContext * _clutter_context_create_pango_context (ClutterMainContext *self) { PangoContext *context; - if (G_LIKELY (self->pango_context != NULL)) - context = self->pango_context; - else - { - context = cogl_pango_font_map_create_context (self->font_map); - self->pango_context = context; - } - - g_signal_connect (self->backend, "resolution-changed", - G_CALLBACK (update_pango_context), - self); - g_signal_connect (self->backend, "font-changed", - G_CALLBACK (update_pango_context), - self); - + context = cogl_pango_font_map_create_context (self->font_map); update_pango_context (self->backend, self); return context; diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 383b7df88..c65bfb066 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -136,6 +136,7 @@ struct _ClutterMainContext #define CLUTTER_CONTEXT() (clutter_context_get_default ()) ClutterMainContext *clutter_context_get_default (void); PangoContext *_clutter_context_create_pango_context (ClutterMainContext *self); +PangoContext *_clutter_context_get_pango_context (ClutterMainContext *self); #define CLUTTER_PRIVATE_FLAGS(a) (((ClutterActor *) (a))->private_flags) #define CLUTTER_SET_PRIVATE_FLAGS(a,f) (CLUTTER_PRIVATE_FLAGS (a) |= (f)) From 982a678053a518e94928e5d7a344c4c5525dec15 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 15:29:10 +0000 Subject: [PATCH 090/108] Add ClutterActor::create_pango_context() Sometimes an actor needs to set specific font rendering options on the PangoContext without changing settings for every other text-rendering actor. In order to do this, we need a new public method to create a Pango context object -- preset with all the default settings -- owned by the developer and not shared with the rest of Clutter. This new method is called clutter_actor_create_pango_context(); while it does not strictly depend on a ClutterActor, it is a good idea to have it inside the ClutterActor API to map the current get_pango_context() method and in case we start storing screen-specific data to the Actor itself during the 1.x API cycle. --- clutter/clutter-actor.c | 36 +++++++++++++++++++++++++++++++++--- clutter/clutter-actor.h | 31 ++++++++++++++++--------------- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index b4068b5d2..49da27403 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7606,8 +7606,9 @@ clutter_actor_grab_key_focus (ClutterActor *self) * is already configured using the appropriate font map, resolution * and font options. * - * The returned #PangoContext will be updated each time the options - * stored by the default #ClutterBackend change. + * Unlike clutter_actor_create_pango_context(), this context is owend + * by the #ClutterActor and it will be updated each time the options + * stored by the #ClutterBackend change. * * You can use the returned #PangoContext to create a #PangoLayout * and render text using cogl_pango_render_layout() to reuse the @@ -7633,8 +7634,37 @@ clutter_actor_get_pango_context (ClutterActor *self) return priv->pango_context; ctx = CLUTTER_CONTEXT (); - priv->pango_context = _clutter_context_create_pango_context (ctx); + priv->pango_context = _clutter_context_get_pango_context (ctx); g_object_ref (priv->pango_context); return priv->pango_context; } + +/** + * clutter_actor_create_pango_context: + * @self: a #ClutterActor + * + * Creates a #PangoContext for the given actor. The #PangoContext + * is already configured using the appropriate font map, resolution + * and font options. + * + * See also clutter_actor_get_pango_context(). + * + * Return value: the newly created #PangoContext. Use g_object_ref() + * on the returned value to deallocate its resources + * + * Since: 1.0 + */ +PangoContext * +clutter_actor_create_pango_context (ClutterActor *self) +{ + ClutterMainContext *ctx; + PangoContext *retval; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); + + ctx = CLUTTER_CONTEXT (); + retval = _clutter_context_create_pango_context (ctx); + + return retval; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 9461e9fa3..af962caf1 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -422,6 +422,8 @@ void clutter_actor_set_opacity (ClutterActor guint8 opacity); guint8 clutter_actor_get_opacity (ClutterActor *self); guint8 clutter_actor_get_paint_opacity (ClutterActor *self); +gboolean clutter_actor_get_paint_visibility (ClutterActor *self); + void clutter_actor_set_name (ClutterActor *self, const gchar *name); @@ -547,25 +549,24 @@ gboolean clutter_actor_is_rotated (ClutterActor *self); gboolean clutter_actor_is_scaled (ClutterActor *self); gboolean clutter_actor_should_pick_paint (ClutterActor *self); -void clutter_actor_box_get_from_vertices (ClutterVertex vtx[4], - ClutterActorBox *box); +void clutter_actor_box_get_from_vertices (ClutterVertex vtx[4], + ClutterActorBox *box); -void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, - ClutterVertex verts[4]); +void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, + ClutterVertex verts[4]); -void clutter_actor_apply_transform_to_point (ClutterActor *self, - const ClutterVertex *point, - ClutterVertex *vertex); -void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, - ClutterActor *ancestor, - const ClutterVertex *point, - ClutterVertex *vertex); +void clutter_actor_apply_transform_to_point (ClutterActor *self, + const ClutterVertex *point, + ClutterVertex *vertex); +void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, + ClutterActor *ancestor, + const ClutterVertex *point, + ClutterVertex *vertex); -gboolean clutter_actor_get_paint_visibility (ClutterActor *self); +void clutter_actor_grab_key_focus (ClutterActor *self); -void clutter_actor_grab_key_focus (ClutterActor *self); - -PangoContext *clutter_actor_get_pango_context (ClutterActor *self); +PangoContext *clutter_actor_get_pango_context (ClutterActor *self); +PangoContext *clutter_actor_create_pango_context (ClutterActor *self); G_END_DECLS From c988b7b736e6d7ab7929b409f6c54c81d5862623 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:25:50 +0000 Subject: [PATCH 091/108] Remove BindingPool::list_actions() The clutter_binding_pool_list_actions() was not implemented. The utility of a call listing all the action names is also debatable: all the functions related to the key bindings take the key symbol and modifiers -- except the block_action() and unblock_action() pair. --- clutter/clutter-binding-pool.c | 6 ------ clutter/clutter-binding-pool.h | 1 - doc/reference/clutter/clutter-sections.txt | 1 - 3 files changed, 8 deletions(-) diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index de31870b3..065310bcf 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -601,12 +601,6 @@ clutter_binding_pool_override_closure (ClutterBindingPool *pool, } } -gchar ** -clutter_binding_pool_list_actions (ClutterBindingPool *pool) -{ - return NULL; -} - /** * clutter_binding_pool_find_action: * @pool: a #ClutterBindingPool diff --git a/clutter/clutter-binding-pool.h b/clutter/clutter-binding-pool.h index 6e2dac4bb..55b5273c8 100644 --- a/clutter/clutter-binding-pool.h +++ b/clutter/clutter-binding-pool.h @@ -83,7 +83,6 @@ void clutter_binding_pool_override_closure (ClutterBindingPool ClutterModifierType modifiers, GClosure *closure); -gchar ** clutter_binding_pool_list_actions (ClutterBindingPool *pool); G_CONST_RETURN gchar *clutter_binding_pool_find_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers); diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 7883e2e9c..a13b51d2f 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1593,7 +1593,6 @@ clutter_binding_pool_install_action clutter_binding_pool_install_closure clutter_binding_pool_override_action clutter_binding_pool_override_closure -clutter_binding_pool_list_actions clutter_binding_pool_find_action clutter_binding_pool_remove_action clutter_binding_pool_block_action From 700b34148bd9e052bf521caeaff05c1373200972 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:29:49 +0000 Subject: [PATCH 092/108] Remove the binding pool entry from the list When removing a binding entry from the binding pool we should not only remove it from the hash table, but also from the linked list we use to iterate over inside the block/unblock_action() pair. --- clutter/clutter-binding-pool.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clutter/clutter-binding-pool.c b/clutter/clutter-binding-pool.c index 065310bcf..28082cb63 100644 --- a/clutter/clutter-binding-pool.c +++ b/clutter/clutter-binding-pool.c @@ -650,6 +650,7 @@ clutter_binding_pool_remove_action (ClutterBindingPool *pool, ClutterModifierType modifiers) { ClutterBindingEntry remove_entry = { 0, }; + GSList *l; g_return_if_fail (pool != NULL); g_return_if_fail (key_val != 0); @@ -659,6 +660,18 @@ clutter_binding_pool_remove_action (ClutterBindingPool *pool, remove_entry.key_val = key_val; remove_entry.modifiers = modifiers; + for (l = pool->entries; l != NULL; l = l->data) + { + ClutterBindingEntry *e = l->data; + + if (e->key_val == remove_entry.key_val && + e->modifiers == remove_entry.modifiers) + { + pool->entries = g_slist_remove_link (pool->entries, l); + break; + } + } + g_hash_table_remove (pool->entries_hash, &remove_entry); } From c79112bd3c4febc39eeba5cabe50319f1eb7976c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:44:52 +0000 Subject: [PATCH 093/108] Revert the logic of the PangoContext check The branch that creates the global PangoContext should only run if there is no global PangoContext already. --- clutter/clutter-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 588b00a84..12bef383c 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -452,7 +452,7 @@ update_pango_context (ClutterBackend *backend, PangoContext * _clutter_context_get_pango_context (ClutterMainContext *self) { - if (G_LIKELY (self->pango_context)) + if (G_UNLIKELY (self->pango_context == NULL)) { PangoContext *context; From a5f9c7269465da32a8f0209dfce27f875e4fe05b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:45:59 +0000 Subject: [PATCH 094/108] Pass the PangoContext, not the MainContext When updating the PangoContext with the current options (font name, options, resolution) pass the PangoContext instead of the Clutter MainContext structure pointer. --- clutter/clutter-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 12bef383c..6dc8cbb94 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -478,7 +478,7 @@ _clutter_context_create_pango_context (ClutterMainContext *self) PangoContext *context; context = cogl_pango_font_map_create_context (self->font_map); - update_pango_context (self->backend, self); + update_pango_context (self->backend, context); return context; } From 11870040998e0c7e0a30da11a1b91e0c54631c5b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 5 Jan 2009 16:48:46 +0000 Subject: [PATCH 095/108] Clean up the update_pango_context() function Logically split the various operations with whitespace so that it's clear what does what. --- clutter/clutter-main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 6dc8cbb94..2bd6a7dee 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -432,6 +432,10 @@ update_pango_context (ClutterBackend *backend, const gchar *font_name; gdouble resolution; + /* update the text direction */ + pango_context_set_base_dir (context, clutter_text_direction); + + /* get the configuration for the PangoContext from the backend */ font_name = clutter_backend_get_font_name (backend); font_options = clutter_backend_get_font_options (backend); resolution = clutter_backend_get_resolution (backend); @@ -442,7 +446,6 @@ update_pango_context (ClutterBackend *backend, resolution = 96.0; /* fall back */ pango_context_set_font_description (context, font_desc); - pango_context_set_base_dir (context, clutter_text_direction); pango_cairo_context_set_font_options (context, font_options); pango_cairo_context_set_resolution (context, resolution); From 1892f8cb1da5727e56323a42f7c2ff9b5d596c31 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 12:35:19 +0000 Subject: [PATCH 096/108] Allow localizations to change the text direction The locale translators of Clutter are also the ones that should set the default direction of the text in a Clutter user interface. This commit adds a translatable string that defines the direction of the text; the translation authors will change it to the correct value and that will determine the default direction. The default text direction can be overridden by using the CLUTTER_TEXT_DIRECTION environment variable, or by using the --clutter-text-direction command line switch. In any other case, the locale will determine the text direction, as it should. --- clutter/clutter-main.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 2bd6a7dee..38ed3e5cf 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -406,19 +406,34 @@ _clutter_do_pick (ClutterStage *stage, static PangoDirection clutter_get_text_direction (void) { - const gchar *direction; PangoDirection dir = PANGO_DIRECTION_LTR; + const gchar *direction; direction = g_getenv ("CLUTTER_TEXT_DIRECTION"); if (direction && *direction != '\0') { if (strcmp (direction, "rtl") == 0) dir = PANGO_DIRECTION_RTL; - else + else if (strcmp (direction, "ltr") == 0) dir = PANGO_DIRECTION_LTR; } else - dir = PANGO_DIRECTION_LTR; + { + /* Translate to default:RTL if you want your widgets + * to be RTL, otherwise translate to default:LTR. + * + * Do *not* translate it to "predefinito:LTR": if it + * it isn't default:LTR or default:RTL it will not work + */ + char *e = _("default:LTR"); + + if (strcmp (e, "default:RTL") == 0) + dir = PANGO_DIRECTION_RTL; + else if (strcmp (e, "default:LTR") == 0) + dir = PANGO_DIRECTION_LTR; + else + g_warning ("Whoever translated default:LTR did so wrongly."); + } return dir; } From 979b6794bb207575fdac6fa520cca5c9adf6f86e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 15:22:31 +0000 Subject: [PATCH 097/108] [text] Coalesce text visibility and password character Using two properties to set a password entry can be construed as both cumbersome and a gtk-ism. And rightly so on both counts. The :text-visible property has also conflicting semantics with the :cursor-visible one: while the latter hides the cursor, the former changes the display of the contents of the Text actor. It is, thus, not a matter of "visibility" but of "rendering". Instead of setting the :text-visible and :invisible-char properties to have a password text field, the Text actor should just have a single :password-char property holding a Unicode character. If the value of the :password-char is non-zero, the Text actor will use the Unicode character to render the contents of the text entry. This commit removes the following methods: clutter_text_set_text_visible() clutter_text_get_text_visible() clutter_text_set_invisible_char() clutter_text_get_invisible_char() And the following properties: ClutterText:text-visible ClutterText:invisible-char In favour of: clutter_text_set_password_char() clutter_text_get_password_char() And: ClutterText:password-char Thus making obvious what use the property and accessor methods are for and simplifying the process of creating a simple password text field to: text = clutter_text_new (); clutter_text_set_password_char (CLUTTER_TEXT (text), '*'); --- clutter/clutter-text.c | 177 ++++++++++------------------------------- clutter/clutter-text.h | 7 +- 2 files changed, 46 insertions(+), 138 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index e90491722..0b8deb153 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -123,7 +123,6 @@ struct _ClutterTextPrivate guint selectable : 1; guint in_select_drag : 1; guint cursor_color_set : 1; - guint text_visible : 1; /* current cursor position */ gint position; @@ -149,7 +148,7 @@ struct _ClutterTextPrivate gint max_length; - gunichar priv_char; + gunichar password_char; }; enum @@ -175,8 +174,7 @@ enum PROP_EDITABLE, PROP_SELECTABLE, PROP_ACTIVATABLE, - PROP_TEXT_VISIBLE, - PROP_INVISIBLE_CHAR, + PROP_PASSWORD_CHAR, PROP_MAX_LENGTH }; @@ -261,7 +259,7 @@ clutter_text_create_layout_no_cache (ClutterText *text, { if (!priv->use_markup) { - if (priv->text_visible) + if (G_LIKELY (priv->password_char == 0)) pango_layout_set_text (layout, priv->text, priv->n_bytes); else { @@ -270,10 +268,7 @@ clutter_text_create_layout_no_cache (ClutterText *text, gchar buf[7]; gint char_len, i; - if (priv->priv_char != 0) - invisible_char = priv->priv_char; - else - invisible_char = ' '; + invisible_char = priv->password_char; /* we need to convert the string built of invisible * characters into UTF-8 for it to be fed to the Pango @@ -459,20 +454,18 @@ clutter_text_position_to_coords (ClutterText *self, { ClutterTextPrivate *priv = self->priv; PangoRectangle rect; - gint priv_char_bytes; + gint password_char_bytes = 1; gint index_; - if (!priv->text_visible && priv->priv_char) - priv_char_bytes = g_unichar_to_utf8 (priv->priv_char, NULL); - else - priv_char_bytes = 1; + if (priv->password_char != 0) + password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL); if (position == -1) { - if (priv->text_visible) + if (priv->password_char == 0) index_ = priv->n_bytes; else - index_ = priv->n_chars * priv_char_bytes; + index_ = priv->n_chars * password_char_bytes; } else if (position == 0) { @@ -480,10 +473,10 @@ clutter_text_position_to_coords (ClutterText *self, } else { - if (priv->text_visible) + if (priv->password_char == 0) index_ = offset_to_bytes (priv->text, position); else - index_ = priv->position * priv_char_bytes; + index_ = priv->position * password_char_bytes; } pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_, @@ -634,12 +627,8 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_selectable (self, g_value_get_boolean (value)); break; - case PROP_TEXT_VISIBLE: - clutter_text_set_text_visible (self, g_value_get_boolean (value)); - break; - - case PROP_INVISIBLE_CHAR: - clutter_text_set_invisible_char (self, g_value_get_uint (value)); + case PROP_PASSWORD_CHAR: + clutter_text_set_password_char (self, g_value_get_uint (value)); break; case PROP_MAX_LENGTH: @@ -709,12 +698,8 @@ clutter_text_get_property (GObject *gobject, g_value_set_boolean (value, priv->activatable); break; - case PROP_TEXT_VISIBLE: - g_value_set_boolean (value, priv->text_visible); - break; - - case PROP_INVISIBLE_CHAR: - g_value_set_uint (value, priv->priv_char); + case PROP_PASSWORD_CHAR: + g_value_set_uint (value, priv->password_char); break; case PROP_MAX_LENGTH: @@ -1791,38 +1776,20 @@ clutter_text_class_init (ClutterTextClass *klass) g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); /** - * ClutterText:text-visible: + * ClutterText:password-char: * - * Whether the contents of the #ClutterText actor should be visible - * or substituted by a Unicode character, like in password text fields. - * The Unicode character is set using #ClutterText:invisible-char. + * If non-zero, the character that should be used in place of + * the actual text in a password text actor. * * Since: 1.0 */ - pspec = g_param_spec_boolean ("text-visible", - "Text Visible", - "Whether the text should be visible " - "or subsituted with an invisible " - "Unicode character", - FALSE, + pspec = g_param_spec_unichar ("password-char", + "Password Character", + "If non-zero, use this character to " + "display the actor's contents", + 0, CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_TEXT_VISIBLE, pspec); - - /** - * ClutterText:invisible-char: - * - * The Unicode character used to render the contents of the #ClutterText - * actor if #ClutterText:text-invisible is set to %TRUE. - * - * Since: 1.0 - */ - pspec = g_param_spec_unichar ("invisible-char", - "Invisible Character", - "The Unicode character used when the " - "text is set as not visible", - '*', - CLUTTER_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_INVISIBLE_CHAR, pspec); + g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec); /** * ClutterText:max-length: @@ -2015,8 +1982,7 @@ clutter_text_init (ClutterText *self) priv->cursor_color_set = FALSE; - priv->text_visible = TRUE; - priv->priv_char = '*'; + priv->password_char = 0; priv->max_length = 0; @@ -3290,23 +3256,21 @@ clutter_text_get_cursor_size (ClutterText *self) } /** - * clutter_text_set_text_visible: + * clutter_text_set_password_char: * @self: a #ClutterText - * @visible: %TRUE if the contents of the actor are displayed as plain text. + * @wc: a Unicode character, or 0 * - * Sets whether the contents of the text actor are visible or not. When - * visibility is set to %FALSE, characters are displayed as the invisible - * char, and will also appear that way when the text in the text actor is - * copied elsewhere. + * Sets the character to use in place of the actual text in a + * password text actor. * - * The default invisible char is the asterisk '*', but it can be changed with - * clutter_text_set_invisible_char(). + * If @wc is 0 the text will be displayed as it is entered in the + * #ClutterText actor. * * Since: 1.0 */ void -clutter_text_set_text_visible (ClutterText *self, - gboolean visible) +clutter_text_set_password_char (ClutterText *self, + gunichar wc) { ClutterTextPrivate *priv; @@ -3314,88 +3278,35 @@ clutter_text_set_text_visible (ClutterText *self, priv = self->priv; - if (priv->text_visible != visible) + if (priv->password_char != wc) { - priv->text_visible = visible; + priv->password_char = wc; clutter_text_dirty_cache (self); - clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); - g_object_notify (G_OBJECT (self), "text-visible"); + g_object_notify (G_OBJECT (self), "password-char"); } } /** - * clutter_text_get_text_visible: + * clutter_text_get_password_char: * @self: a #ClutterText * - * Retrieves the actor's text visibility. + * Retrieves the character to use in place of the actual text + * as set by clutter_text_set_password_char(). * - * Return value: %TRUE if the contents of the actor are displayed as plaintext - * - * Since: 1.0 - */ -gboolean -clutter_text_get_text_visible (ClutterText *self) -{ - g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); - - return self->priv->text_visible; -} - -/** - * clutter_text_set_invisible_char: - * @self: a #ClutterText - * @wc: a Unicode character - * - * Sets the character to use in place of the actual text when - * clutter_text_set_text_visible() has been called to set text visibility - * to %FALSE. i.e. this is the character used in "password mode" to show the - * user how many characters have been typed. The default invisible char is an - * asterisk ('*'). If you set the invisible char to 0, then the user will get - * no feedback at all: there will be no text on the screen as they type. - * - * Since: 1.0 - */ -void -clutter_text_set_invisible_char (ClutterText *self, - gunichar wc) -{ - ClutterTextPrivate *priv; - - g_return_if_fail (CLUTTER_IS_TEXT (self)); - - priv = self->priv; - - priv->priv_char = wc; - - if (priv->text_visible) - { - clutter_text_dirty_cache (self); - clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); - } - - g_object_notify (G_OBJECT (self), "invisible-char"); -} - -/** - * clutter_text_get_invisible_char: - * @self: a #ClutterText - * - * Returns the character to use in place of the actual text when - * the #ClutterText:text-visibility property is set to %FALSE. - * - * Return value: a Unicode character + * Return value: a Unicode character or 0 if the password + * character is not set * * Since: 1.0 */ gunichar -clutter_text_get_invisible_char (ClutterText *self) +clutter_text_get_password_char (ClutterText *self) { - g_return_val_if_fail (CLUTTER_IS_TEXT (self), '*'); + g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); - return self->priv->priv_char; + return self->priv->password_char; } /** diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index bd63478ee..a194e2525 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -181,12 +181,9 @@ void clutter_text_set_selection (ClutterText *self gssize start_pos, gssize end_pos); gchar * clutter_text_get_selection (ClutterText *self); -void clutter_text_set_text_visible (ClutterText *self, - gboolean visible); -gboolean clutter_text_get_text_visible (ClutterText *self); -void clutter_text_set_invisible_char (ClutterText *self, +void clutter_text_set_password_char (ClutterText *self, gunichar wc); -gunichar clutter_text_get_invisible_char (ClutterText *self); +gunichar clutter_text_get_password_char (ClutterText *self); void clutter_text_set_max_length (ClutterText *self, gint max); gint clutter_text_get_max_length (ClutterText *self); From c4475c6bfc133eea9e44abea73c3fa2f121fded8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 15:29:44 +0000 Subject: [PATCH 098/108] [docs] Update the Text section After the 979b6794 commit, the section for ClutterText needs updating on the renamed and removed accessors. --- doc/reference/clutter/clutter-sections.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index a13b51d2f..52934b014 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1626,8 +1626,8 @@ clutter_text_set_ellipsize clutter_text_get_ellipsize clutter_text_set_font_name clutter_text_get_font_name -clutter_text_set_invisible_char -clutter_text_get_invisible_char +clutter_text_set_password_char +clutter_text_get_password_char clutter_text_set_justify clutter_text_get_justify clutter_text_get_layout @@ -1643,8 +1643,6 @@ clutter_text_set_selection clutter_text_get_selection clutter_text_set_selection_bound clutter_text_get_selection_bound -clutter_text_set_text_visible -clutter_text_get_text_visible clutter_text_set_use_markup clutter_text_get_use_markup From 87ab64d291781d468c55c8dd54dd9182b80921fd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 15:30:31 +0000 Subject: [PATCH 099/108] [tests] Add unit for the ClutterText:password-char property Check that the contents of the Text actor are unaffected by the :password-char property; that the accessors are correct; and finally that the initial value for a newly constructed Text actor is valid. --- tests/conform/test-clutter-text.c | 19 +++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 2 files changed, 20 insertions(+) diff --git a/tests/conform/test-clutter-text.c b/tests/conform/test-clutter-text.c index 20cdb538c..6814013a8 100644 --- a/tests/conform/test-clutter-text.c +++ b/tests/conform/test-clutter-text.c @@ -301,6 +301,25 @@ test_text_delete_text (TestConformSimpleFixture *fixture, clutter_actor_destroy (CLUTTER_ACTOR (text)); } +void +test_text_password_char (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); + + g_assert_cmpint (clutter_text_get_password_char (text), ==, 0); + + clutter_text_set_text (text, "hello"); + g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); + + clutter_text_set_password_char (text, '*'); + g_assert_cmpint (clutter_text_get_password_char (text), ==, '*'); + + g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); + + clutter_actor_destroy (CLUTTER_ACTOR (text)); +} + static void init_event (ClutterKeyEvent *event) { diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index abf6280b2..1b8d54c0f 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -82,6 +82,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/text", test_text_event); TEST_CONFORM_SIMPLE ("/text", test_text_get_chars); TEST_CONFORM_SIMPLE ("/text", test_text_cache); + TEST_CONFORM_SIMPLE ("/text", test_text_password_char); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_size); TEST_CONFORM_SIMPLE ("/rectangle", test_rect_set_color); From 3d32d464e9a83ca2a89700778ece28307f4d359e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 20:52:03 +0000 Subject: [PATCH 100/108] [text] Use cached length when possible Since clutter_text_set_text() measures the length of the text each time, we should use the cached length instead of recomputing the text length each time. This should save us some time when dealing with long, multi-byte texts. --- clutter/clutter-text.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 0b8deb153..da74de1c6 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1143,7 +1143,7 @@ clutter_text_real_move_left (ClutterText *self, gint pos = priv->position; gint len; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (pos != 0 && len !=0) { @@ -1169,7 +1169,7 @@ clutter_text_real_move_right (ClutterText *self, gint pos = priv->position; gint len; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (pos != -1 && len !=0) { @@ -3190,7 +3190,7 @@ clutter_text_set_cursor_position (ClutterText *self, priv = self->priv; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (position < 0 || position >= len) priv->position = -1; @@ -3335,7 +3335,7 @@ clutter_text_set_max_length (ClutterText *self, if (priv->max_length != max) { if (max < 0) - max = g_utf8_strlen (priv->text, -1); + max = priv->n_chars; priv->max_length = max; @@ -3528,7 +3528,7 @@ clutter_text_delete_chars (ClutterText *self, if (!priv->text) return; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; new = g_string_new (priv->text); if (priv->position == -1) From 8182b354b167681a89ef9c3354c1278378e4ea2c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 6 Jan 2009 20:54:20 +0000 Subject: [PATCH 101/108] [text] Fix the deletion actions When using the delete-prev action from the end of the text we end up either missing the first glyph we have to delete or falling through the last one in the text. This commit fixes both issues. --- clutter/clutter-text.c | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index da74de1c6..a49312616 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -1388,16 +1388,12 @@ clutter_text_real_del_next (ClutterText *self, return TRUE; pos = priv->position; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (len && pos != -1 && pos < len) - { - clutter_text_delete_text (self, pos, pos + 1); + clutter_text_delete_text (self, pos, pos + 1); - return TRUE; - } - - return FALSE; + return TRUE; } static gboolean @@ -1414,7 +1410,7 @@ clutter_text_real_del_prev (ClutterText *self, return TRUE; pos = priv->position; - len = g_utf8_strlen (priv->text, -1); + len = priv->n_chars; if (pos != 0 && len != 0) { @@ -1422,19 +1418,19 @@ clutter_text_real_del_prev (ClutterText *self, { clutter_text_set_cursor_position (self, len - 1); clutter_text_set_selection_bound (self, len - 1); + + clutter_text_delete_text (self, len - 1, len); } else { clutter_text_set_cursor_position (self, pos - 1); clutter_text_set_selection_bound (self, pos - 1); + + clutter_text_delete_text (self, pos - 1, pos); } - - clutter_text_delete_text (self, pos - 1, pos); - - return TRUE; } - return FALSE; + return TRUE; } static gboolean @@ -3480,18 +3476,15 @@ clutter_text_delete_text (ClutterText *self, if (!priv->text) return; - if (end_pos == -1) - { - start_bytes = offset_to_bytes (priv->text, - g_utf8_strlen (priv->text, -1) - 1); - end_bytes = offset_to_bytes (priv->text, - g_utf8_strlen (priv->text, -1)); - } + if (start_pos == 0) + start_bytes = 0; else - { - start_bytes = offset_to_bytes (priv->text, start_pos); - end_bytes = offset_to_bytes (priv->text, end_pos); - } + start_bytes = offset_to_bytes (priv->text, start_pos); + + if (end_pos == -1) + end_bytes = offset_to_bytes (priv->text, priv->n_chars); + else + end_bytes = offset_to_bytes (priv->text, end_pos); new = g_string_new (priv->text); new = g_string_erase (new, start_bytes, end_bytes - start_bytes); From 43f82332dd918c7e13e7896523a2508430cfa6f0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:25:24 +0000 Subject: [PATCH 102/108] [text] Add single-line-mode to ClutterText Allow using ClutterText as a single line text field. This is useful for text fields that accept just a single line of contents by default, and respond to the Enter key press to execute some action. The :single-line-mode property enables this behaviour inside ClutterText by clipping and scrolling the contents of the PangoLayout if they do not fit the allocated width of the Text actor. --- clutter/clutter-text.c | 175 +++++++++++++++++++++++++++++++++++++++-- clutter/clutter-text.h | 4 + 2 files changed, 172 insertions(+), 7 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index a49312616..8a3357652 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -135,6 +135,8 @@ struct _ClutterTextPrivate */ gint x_pos; + gint text_x; + /* the length of the text, in bytes */ gint n_bytes; @@ -175,7 +177,8 @@ enum PROP_SELECTABLE, PROP_ACTIVATABLE, PROP_PASSWORD_CHAR, - PROP_MAX_LENGTH + PROP_MAX_LENGTH, + PROP_SINGLE_LINE_MODE }; enum @@ -635,6 +638,10 @@ clutter_text_set_property (GObject *gobject, clutter_text_set_max_length (self, g_value_get_int (value)); break; + case PROP_SINGLE_LINE_MODE: + clutter_text_set_single_line_mode (self, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -706,6 +713,10 @@ clutter_text_get_property (GObject *gobject, g_value_set_int (value, priv->max_length); break; + case PROP_SINGLE_LINE_MODE: + g_value_set_boolean (value, priv->single_line_mode); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -769,8 +780,6 @@ cursor_paint (ClutterText *self) real_opacity); } - clutter_text_ensure_cursor_position (self); - if (priv->position == 0) priv->cursor_pos.x -= 2; @@ -997,6 +1006,8 @@ clutter_text_key_press (ClutterActor *actor, return FALSE; } +#define TEXT_PADDING 2 + static void clutter_text_paint (ClutterActor *self) { @@ -1006,6 +1017,8 @@ clutter_text_paint (ClutterActor *self) ClutterActorBox alloc = { 0, }; CoglColor color = { 0, }; guint8 real_opacity; + gint text_x = priv->text_x; + gboolean clip_set = FALSE; if (priv->font_desc == NULL || priv->text == NULL) { @@ -1015,23 +1028,81 @@ clutter_text_paint (ClutterActor *self) return; } - cursor_paint (text); - - CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text); + clutter_text_ensure_cursor_position (text); clutter_actor_get_allocation_box (self, &alloc); layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1); + if (priv->single_line_mode) + { + PangoRectangle logical_rect = { 0, }; + gint actor_width, text_width; + + pango_layout_get_extents (layout, NULL, &logical_rect); + + cogl_clip_set (0, 0, + CLUTTER_UNITS_TO_FIXED (alloc.x2 - alloc.x1), + CLUTTER_UNITS_TO_FIXED (alloc.y2 - alloc.y1)); + clip_set = TRUE; + + actor_width = (CLUTTER_UNITS_TO_DEVICE (alloc.x2 - alloc.x1)) + - 2 * TEXT_PADDING; + text_width = logical_rect.width / PANGO_SCALE; + + if (actor_width < text_width) + { + gint cursor_x = priv->cursor_pos.x; + + if (priv->position == -1) + { + text_x = actor_width - text_width; + priv->cursor_pos.x += text_x + TEXT_PADDING; + } + else if (priv->position == 0) + { + text_x = 0; + } + else + { + if (text_x <= 0) + { + gint diff = -1 * text_x; + + if (cursor_x < diff) + text_x += diff - cursor_x; + else if (cursor_x > (diff + actor_width)) + text_x -= cursor_x - (diff - actor_width); + } + } + } + else + { + text_x = 0; + priv->cursor_pos.x += text_x + TEXT_PADDING; + } + } + else + text_x = 0; + + cursor_paint (text); + real_opacity = clutter_actor_get_paint_opacity (self) * priv->text_color.alpha / 255; + CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text); + cogl_color_set_from_4ub (&color, priv->text_color.red, priv->text_color.green, priv->text_color.blue, real_opacity); - cogl_pango_render_layout (layout, 0, 0, &color, 0); + cogl_pango_render_layout (layout, text_x, 0, &color, 0); + + if (clip_set) + cogl_clip_unset (); + + priv->text_x = text_x; } static void @@ -1801,6 +1872,26 @@ clutter_text_class_init (ClutterTextClass *klass) CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec); + /** + * ClutterText:single-line-mode: + * + * Whether the #ClutterText actor should be in single line mode + * or not. A single line #ClutterText actor will only contain a + * single line of text, scrolling it in case its length is bigger + * than the allocated size. + * + * Setting this property will also set the #ClutterText:activatable + * property as a side-effect. + * + * Since: 1.0 + */ + pspec = g_param_spec_boolean ("single-line-mode", + "Single Line Mode", + "Whether the text should be a single line", + FALSE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec); + /** * ClutterText::text-changed: * @self: the #ClutterText that emitted the signal @@ -3588,3 +3679,73 @@ clutter_text_get_chars (ClutterText *self, return g_strndup (priv->text + start_index, end_index - start_index); } + +/** + * clutter_text_set_single_line_mode: + * @self: a #ClutterText + * @single_line: whether to enable single line mode + * + * Sets whether a #ClutterText actor should be in single line mode + * or not. + * + * A text actor in single line mode will not wrap text and will clip + * the the visible area to the predefined size. The contents of the + * text actor will scroll to display the end of the text if its length + * is bigger than the allocated width. + * + * When setting the single line mode the #ClutterText:activatable + * property is also set as a side effect. Instead of entering a new + * line character, the text actor will emit the #ClutterText::activate + * signal. + * + * Since: 1.0 + */ +void +clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line) +{ + ClutterTextPrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXT (self)); + + priv = self->priv; + + if (priv->single_line_mode != single_line) + { + g_object_freeze_notify (G_OBJECT (self)); + + priv->single_line_mode = single_line; + + if (priv->single_line_mode) + { + priv->activatable = TRUE; + + g_object_notify (G_OBJECT (self), "activatable"); + } + + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); + + g_object_notify (G_OBJECT (self), "single-line-mode"); + + g_object_thaw_notify (G_OBJECT (self)); + } +} + +/** + * clutter_text_get_single_line_mode: + * @self: a #ClutterText + * + * Retrieves whether the #ClutterText actor is in single line mode. + * + * Return value: %TRUE if the #ClutterText actor is in single line mode + * + * Since: 1.0 + */ +gboolean +clutter_text_get_single_line_mode (ClutterText *self) +{ + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + return self->priv->single_line_mode; +} diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index a194e2525..57a3b2ed7 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -187,6 +187,10 @@ gunichar clutter_text_get_password_char (ClutterText *self void clutter_text_set_max_length (ClutterText *self, gint max); gint clutter_text_get_max_length (ClutterText *self); +void clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line); +gboolean clutter_text_get_single_line_mode (ClutterText *self); + gboolean clutter_text_activate (ClutterText *self); G_END_DECLS From 71c03df967e0725aa49f2a51de9909b2fd42d971 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:27:50 +0000 Subject: [PATCH 103/108] [tests] Add text field interactive test The test-text-field is a test/example that shows how to use the ClutterText as a text input field in single line mode. --- tests/interactive/Makefile.am | 3 +- tests/interactive/test-text-field.c | 117 ++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/interactive/test-text-field.c diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index c07f48fa4..068bb7860 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -37,7 +37,8 @@ UNIT_TESTS = \ test-animation.c \ test-easing.c \ test-binding-pool.c \ - test-text.c + test-text.c \ + test-text-field.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-text-field.c b/tests/interactive/test-text-field.c new file mode 100644 index 000000000..ca582731d --- /dev/null +++ b/tests/interactive/test-text-field.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#define FONT "Mono Bold 14px" + +static void +on_entry_paint (ClutterActor *actor, + gpointer data) +{ + ClutterActorBox allocation = { 0, }; + ClutterUnit width, height; + + clutter_actor_get_allocation_box (actor, &allocation); + width = allocation.x2 - allocation.x1; + height = allocation.y2 - allocation.y1; + + cogl_set_source_color4ub (255, 255, 255, 255); + cogl_path_round_rectangle (0, 0, + CLUTTER_UNITS_TO_FIXED (width), + CLUTTER_UNITS_TO_FIXED (height), + COGL_FIXED_FROM_INT (4), + COGL_ANGLE_FROM_DEG (1.0)); + cogl_path_stroke (); +} + +static void +on_entry_activate (ClutterText *text, + gpointer data) +{ + g_print ("Text activated: %s\n", clutter_text_get_text (text)); +} + +static ClutterActor * +create_label (const ClutterColor *color, + const gchar *text) +{ + ClutterActor *retval = clutter_text_new_full (FONT, text, color); + + clutter_text_set_editable (CLUTTER_TEXT (retval), FALSE); + clutter_text_set_selectable (CLUTTER_TEXT (retval), FALSE); + + return retval; +} + +static ClutterActor * +create_entry (const ClutterColor *color, + const gchar *text, + gunichar password_char, + gint max_length) +{ + ClutterActor *retval = clutter_text_new_full (FONT, text, color); + ClutterColor selection = { 0, }; + + clutter_actor_set_width (retval, 200); + clutter_actor_set_reactive (retval, TRUE); + + clutter_color_darken (color, &selection); + + clutter_text_set_editable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_selectable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_activatable (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_single_line_mode (CLUTTER_TEXT (retval), TRUE); + clutter_text_set_password_char (CLUTTER_TEXT (retval), password_char); + clutter_text_set_cursor_color (CLUTTER_TEXT (retval), &selection); + clutter_text_set_max_length (CLUTTER_TEXT (retval), max_length); + + g_signal_connect (retval, "activate", + G_CALLBACK (on_entry_activate), + NULL); + g_signal_connect (retval, "paint", + G_CALLBACK (on_entry_paint), + NULL); + + return retval; +} + +G_MODULE_EXPORT gint +test_text_field_main (gint argc, + gchar **argv) +{ + ClutterActor *stage; + ClutterActor *text; + ClutterColor entry_color = {0x33, 0xff, 0x33, 0xff}; + ClutterColor label_color = {0xff, 0xff, 0xff, 0xff}; + ClutterColor background_color = {0x00, 0x00, 0x00, 0xff}; + gint height; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color); + + text = create_label (&label_color, "Input field: "); + clutter_actor_set_position (text, 10, 10); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + height = clutter_actor_get_height (text); + + text = create_entry (&entry_color, "some text", 0, 0); + clutter_actor_set_position (text, 200, 10); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + text = create_label (&label_color, "Password field: "); + clutter_actor_set_position (text, 10, height + 12); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + text = create_entry (&entry_color, "password", '*', 8); + clutter_actor_set_position (text, 200, height + 12); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), text); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} From 1223fcbb4fd3a0e4ba05a31ca6f71633a56045ee Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:29:41 +0000 Subject: [PATCH 104/108] Update the ignore file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 11ca7ca69..704c9e4ea 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ stamp-h1 /tests/interactive/test-easing /tests/interactive/test-interactive /tests/interactive/test-binding-pool +/tests/interactive/test-text-field /tests/interactive/redhand.png /tests/interactive/test-script.json /tests/conform/test-conformance @@ -171,5 +172,6 @@ stamp-h1 /clutter/x11/stamp-clutter-x11-enum-types.h /po/Makefile.in.in /po/POTFILES +/po/*.pot *.swp *~ From 7f9c384099c1ca839b34f32c0980f76fe87c19e4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:30:59 +0000 Subject: [PATCH 105/108] [docs] Add newly added :single-line-mode accessors Add the ClutterText:single-line-mode property accessor methods to the API reference. --- doc/reference/clutter/clutter-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 52934b014..6157b279c 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1643,6 +1643,8 @@ clutter_text_set_selection clutter_text_get_selection clutter_text_set_selection_bound clutter_text_get_selection_bound +clutter_text_set_single_line_mode +clutter_text_get_single_line_mode clutter_text_set_use_markup clutter_text_get_use_markup From f3142a70dc8e62127a22edf2ff6a8d01aac86329 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 00:43:24 +0000 Subject: [PATCH 106/108] Comments and whitespace fixes to ClutterText --- clutter/clutter-text.c | 20 +++++++++++--------- po/clutter-0.8.pot | 0 2 files changed, 11 insertions(+), 9 deletions(-) delete mode 100644 po/clutter-0.8.pot diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 8a3357652..922acc758 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -130,11 +130,15 @@ struct _ClutterTextPrivate /* current 'other end of selection' position */ gint selection_bound; - /* the x position in the pangolayout, used to + /* the x position in the PangoLayout, used to * avoid drifting when repeatedly moving up|down */ gint x_pos; + /* the x position of the PangoLayout when in + * single line mode, to scroll the contents of the + * text actor + */ gint text_x; /* the length of the text, in bytes */ @@ -192,8 +196,7 @@ enum static guint text_signals[LAST_SIGNAL] = { 0, }; -#define offset_real(t,p) \ - ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) +#define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) static gint offset_to_bytes (const gchar *text, @@ -226,8 +229,7 @@ offset_to_bytes (const gchar *text, return i; } -#define bytes_to_offset(t,p) \ - (g_utf8_pointer_to_offset ((t), (t) + (p))) +#define bytes_to_offset(t,p) (g_utf8_pointer_to_offset ((t), (t) + (p))) static inline void @@ -419,8 +421,8 @@ clutter_text_create_layout (ClutterText *text, static gint clutter_text_coords_to_position (ClutterText *text, - gint x, - gint y) + gint x, + gint y) { gint index_; gint px, py; @@ -1020,7 +1022,7 @@ clutter_text_paint (ClutterActor *self) gint text_x = priv->text_x; gboolean clip_set = FALSE; - if (priv->font_desc == NULL || priv->text == NULL) + if (G_UNLIKELY (priv->font_desc == NULL || priv->text == NULL)) { CLUTTER_NOTE (ACTOR, "desc: %p, text %p", priv->font_desc ? priv->font_desc : 0x0, @@ -3345,7 +3347,7 @@ clutter_text_get_cursor_size (ClutterText *self) /** * clutter_text_set_password_char: * @self: a #ClutterText - * @wc: a Unicode character, or 0 + * @wc: a Unicode character, or 0 to unset the password character * * Sets the character to use in place of the actual text in a * password text actor. diff --git a/po/clutter-0.8.pot b/po/clutter-0.8.pot deleted file mode 100644 index e69de29bb..000000000 From 328534fc95746ddd38d591efa471db142d1793c4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 11:44:54 +0000 Subject: [PATCH 107/108] [text] Fix cursor sizing The cursor should be slightly smaller than the height of the actor, to allow for painting a border. Let's pad it by 1 pixel on the top and 1 on the bottom. Also, we should use the cursor size everywhere and not use hardcoded magic numbers. --- clutter/clutter-text.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 922acc758..ee6fcc68d 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -514,7 +514,7 @@ clutter_text_ensure_cursor_position (ClutterText *self) priv->cursor_pos.x = CLUTTER_UNITS_TO_DEVICE (x); priv->cursor_pos.y = CLUTTER_UNITS_TO_DEVICE (y); priv->cursor_pos.width = priv->cursor_size; - priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height); + priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height) - 2; g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } @@ -783,7 +783,7 @@ cursor_paint (ClutterText *self) } if (priv->position == 0) - priv->cursor_pos.x -= 2; + priv->cursor_pos.x -= priv->cursor_size; if (priv->position == priv->selection_bound) { From ad7d1b54bc86fe523167366583fbf463d15db631 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 7 Jan 2009 11:46:22 +0000 Subject: [PATCH 108/108] Re-align ClutterText header file The addition of the single line mode accessor methods caused the re-alignment of the entire file. --- clutter/clutter-text.h | 182 ++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/clutter/clutter-text.h b/clutter/clutter-text.h index 57a3b2ed7..2dc4bfa04 100644 --- a/clutter/clutter-text.h +++ b/clutter/clutter-text.h @@ -96,102 +96,102 @@ struct _ClutterTextClass GType clutter_text_get_type (void) G_GNUC_CONST; -ClutterActor * clutter_text_new (void); -ClutterActor * clutter_text_new_full (const gchar *font_name, - const gchar *text, - const ClutterColor *color); -ClutterActor * clutter_text_new_with_text (const gchar *font_name, - const gchar *text); +ClutterActor * clutter_text_new (void); +ClutterActor * clutter_text_new_full (const gchar *font_name, + const gchar *text, + const ClutterColor *color); +ClutterActor * clutter_text_new_with_text (const gchar *font_name, + const gchar *text); -G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *self); -void clutter_text_set_text (ClutterText *self, - const gchar *text); -PangoLayout * clutter_text_get_layout (ClutterText *self); -void clutter_text_set_color (ClutterText *self, - const ClutterColor *color); -void clutter_text_get_color (ClutterText *self, - ClutterColor *color); -void clutter_text_set_font_name (ClutterText *self, - const gchar *font_name); -G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *self); +G_CONST_RETURN gchar *clutter_text_get_text (ClutterText *self); +void clutter_text_set_text (ClutterText *self, + const gchar *text); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_font_name (ClutterText *self, + const gchar *font_name); +G_CONST_RETURN gchar *clutter_text_get_font_name (ClutterText *self); -void clutter_text_set_ellipsize (ClutterText *self, - PangoEllipsizeMode mode); -PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); -void clutter_text_set_line_wrap (ClutterText *self, - gboolean line_wrap); -gboolean clutter_text_get_line_wrap (ClutterText *self); -void clutter_text_set_line_wrap_mode (ClutterText *self, - PangoWrapMode wrap_mode); -PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); -PangoLayout * clutter_text_get_layout (ClutterText *self); -void clutter_text_set_attributes (ClutterText *self, - PangoAttrList *attrs); -PangoAttrList * clutter_text_get_attributes (ClutterText *self); -void clutter_text_set_use_markup (ClutterText *self, - gboolean setting); -gboolean clutter_text_get_use_markup (ClutterText *self); -void clutter_text_set_alignment (ClutterText *self, - PangoAlignment alignment); -PangoAlignment clutter_text_get_alignment (ClutterText *self); -void clutter_text_set_justify (ClutterText *self, - gboolean justify); -gboolean clutter_text_get_justify (ClutterText *self); +void clutter_text_set_ellipsize (ClutterText *self, + PangoEllipsizeMode mode); +PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); +void clutter_text_set_line_wrap (ClutterText *self, + gboolean line_wrap); +gboolean clutter_text_get_line_wrap (ClutterText *self); +void clutter_text_set_line_wrap_mode (ClutterText *self, + PangoWrapMode wrap_mode); +PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); +PangoLayout * clutter_text_get_layout (ClutterText *self); +void clutter_text_set_attributes (ClutterText *self, + PangoAttrList *attrs); +PangoAttrList * clutter_text_get_attributes (ClutterText *self); +void clutter_text_set_use_markup (ClutterText *self, + gboolean setting); +gboolean clutter_text_get_use_markup (ClutterText *self); +void clutter_text_set_alignment (ClutterText *self, + PangoAlignment alignment); +PangoAlignment clutter_text_get_alignment (ClutterText *self); +void clutter_text_set_justify (ClutterText *self, + gboolean justify); +gboolean clutter_text_get_justify (ClutterText *self); -void clutter_text_insert_unichar (ClutterText *self, - gunichar wc); -void clutter_text_delete_chars (ClutterText *self, - guint n_chars); -void clutter_text_insert_text (ClutterText *self, - const gchar *text, - gssize position); -void clutter_text_delete_text (ClutterText *self, - gssize start_pos, - gssize end_pos); -gchar * clutter_text_get_chars (ClutterText *self, - gssize start_pos, - gssize end_pos); -void clutter_text_set_editable (ClutterText *self, - gboolean editable); -gboolean clutter_text_get_editable (ClutterText *self); -void clutter_text_set_activatable (ClutterText *self, - gboolean activatable); -gboolean clutter_text_get_activatable (ClutterText *self); +void clutter_text_insert_unichar (ClutterText *self, + gunichar wc); +void clutter_text_delete_chars (ClutterText *self, + guint n_chars); +void clutter_text_insert_text (ClutterText *self, + const gchar *text, + gssize position); +void clutter_text_delete_text (ClutterText *self, + gssize start_pos, + gssize end_pos); +gchar * clutter_text_get_chars (ClutterText *self, + gssize start_pos, + gssize end_pos); +void clutter_text_set_editable (ClutterText *self, + gboolean editable); +gboolean clutter_text_get_editable (ClutterText *self); +void clutter_text_set_activatable (ClutterText *self, + gboolean activatable); +gboolean clutter_text_get_activatable (ClutterText *self); -gint clutter_text_get_cursor_position (ClutterText *self); -void clutter_text_set_cursor_position (ClutterText *self, - gint position); -void clutter_text_set_cursor_visible (ClutterText *self, - gboolean cursor_visible); -gboolean clutter_text_get_cursor_visible (ClutterText *self); -void clutter_text_set_cursor_color (ClutterText *self, - const ClutterColor *color); -void clutter_text_get_cursor_color (ClutterText *self, - ClutterColor *color); -void clutter_text_set_cursor_size (ClutterText *self, - gint size); -guint clutter_text_get_cursor_size (ClutterText *self); -void clutter_text_set_selectable (ClutterText *self, - gboolean selectable); -gboolean clutter_text_get_selectable (ClutterText *self); -void clutter_text_set_selection_bound (ClutterText *self, - gint selection_bound); -gint clutter_text_get_selection_bound (ClutterText *self); -void clutter_text_set_selection (ClutterText *self, - gssize start_pos, - gssize end_pos); -gchar * clutter_text_get_selection (ClutterText *self); -void clutter_text_set_password_char (ClutterText *self, - gunichar wc); -gunichar clutter_text_get_password_char (ClutterText *self); -void clutter_text_set_max_length (ClutterText *self, - gint max); -gint clutter_text_get_max_length (ClutterText *self); -void clutter_text_set_single_line_mode (ClutterText *self, - gboolean single_line); -gboolean clutter_text_get_single_line_mode (ClutterText *self); +gint clutter_text_get_cursor_position (ClutterText *self); +void clutter_text_set_cursor_position (ClutterText *self, + gint position); +void clutter_text_set_cursor_visible (ClutterText *self, + gboolean cursor_visible); +gboolean clutter_text_get_cursor_visible (ClutterText *self); +void clutter_text_set_cursor_color (ClutterText *self, + const ClutterColor *color); +void clutter_text_get_cursor_color (ClutterText *self, + ClutterColor *color); +void clutter_text_set_cursor_size (ClutterText *self, + gint size); +guint clutter_text_get_cursor_size (ClutterText *self); +void clutter_text_set_selectable (ClutterText *self, + gboolean selectable); +gboolean clutter_text_get_selectable (ClutterText *self); +void clutter_text_set_selection_bound (ClutterText *self, + gint selection_bound); +gint clutter_text_get_selection_bound (ClutterText *self); +void clutter_text_set_selection (ClutterText *self, + gssize start_pos, + gssize end_pos); +gchar * clutter_text_get_selection (ClutterText *self); +void clutter_text_set_password_char (ClutterText *self, + gunichar wc); +gunichar clutter_text_get_password_char (ClutterText *self); +void clutter_text_set_max_length (ClutterText *self, + gint max); +gint clutter_text_get_max_length (ClutterText *self); +void clutter_text_set_single_line_mode (ClutterText *self, + gboolean single_line); +gboolean clutter_text_get_single_line_mode (ClutterText *self); -gboolean clutter_text_activate (ClutterText *self); +gboolean clutter_text_activate (ClutterText *self); G_END_DECLS