[gimp] app: implement IM preedit using an overlay widget
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: implement IM preedit using an overlay widget
- Date: Sat, 20 Feb 2010 14:18:24 +0000 (UTC)
commit 4704c187392e6597afa0d00069d3f2f8071d08dd
Author: Michael Natterer <mitch gimp org>
Date: Sat Feb 20 15:13:14 2010 +0100
app: implement IM preedit using an overlay widget
This has several advantages:
- it's always readable, no matter how sick font/colors are.
- it does not mess up the buffer, which is a model that should not
contain temporary edit states.
- preediting does not clutter undo.
- it fixes the remaining bugs in the old preediting code because that
code is completely gone now.
app/tools/gimptexttool-editor.c | 144 +++++++++++++++++++++-------
app/tools/gimptexttool-editor.h | 27 +++---
app/tools/gimptexttool.c | 200 +++++++--------------------------------
app/tools/gimptexttool.h | 3 +
4 files changed, 163 insertions(+), 211 deletions(-)
---
diff --git a/app/tools/gimptexttool-editor.c b/app/tools/gimptexttool-editor.c
index eb93154..c29d7e0 100644
--- a/app/tools/gimptexttool-editor.c
+++ b/app/tools/gimptexttool-editor.c
@@ -74,7 +74,11 @@ static void gimp_text_tool_enter_text (GimpTextTool *text_tool,
static void gimp_text_tool_commit_cb (GtkIMContext *context,
const gchar *str,
GimpTextTool *text_tool);
-static void gimp_text_tool_preedit_changed_cb (GtkIMContext *context,
+static void gimp_text_tool_preedit_start (GtkIMContext *context,
+ GimpTextTool *text_tool);
+static void gimp_text_tool_preedit_end (GtkIMContext *context,
+ GimpTextTool *text_tool);
+static void gimp_text_tool_preedit_changed (GtkIMContext *context,
GimpTextTool *text_tool);
@@ -94,8 +98,14 @@ gimp_text_tool_editor_init (GimpTextTool *text_tool)
g_signal_connect (text_tool->im_context, "commit",
G_CALLBACK (gimp_text_tool_commit_cb),
text_tool);
+ g_signal_connect (text_tool->im_context, "preedit-start",
+ G_CALLBACK (gimp_text_tool_preedit_start),
+ text_tool);
+ g_signal_connect (text_tool->im_context, "preedit-end",
+ G_CALLBACK (gimp_text_tool_preedit_end),
+ text_tool);
g_signal_connect (text_tool->im_context, "preedit-changed",
- G_CALLBACK (gimp_text_tool_preedit_changed_cb),
+ G_CALLBACK (gimp_text_tool_preedit_changed),
text_tool);
}
@@ -270,38 +280,57 @@ gimp_text_tool_reset_im_context (GimpTextTool *text_tool)
}
}
-gchar *
-gimp_text_tool_editor_get_text (GimpTextTool *text_tool)
+void
+gimp_text_tool_editor_get_cursor_rect (GimpTextTool *text_tool,
+ PangoRectangle *cursor_rect,
+ gint *logical_off_x,
+ gint *logical_off_y)
{
- GtkTextBuffer *buffer = text_tool->text_buffer;
- GtkTextIter start, end;
- GtkTextIter selstart, selend;
- gchar *string;
- gchar *fb;
- gchar *lb;
+ GtkTextBuffer *buffer = text_tool->text_buffer;
+ PangoLayout *layout;
+ PangoRectangle ink_extents;
+ PangoRectangle logical_extents;
+ GtkTextIter start;
+ GtkTextIter cursor;
+ gint cursor_index;
+ gchar *string;
- gtk_text_buffer_get_bounds (buffer, &start, &end);
- gtk_text_buffer_get_selection_bounds (buffer, &selstart, &selend);
+ g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
+ g_return_if_fail (cursor_rect != NULL);
- fb = gtk_text_buffer_get_text (buffer, &start, &selstart, TRUE);
- lb = gtk_text_buffer_get_text (buffer, &selstart, &end, TRUE);
+ if (! text_tool->layout)
+ gimp_text_tool_update_layout (text_tool);
- if (text_tool->preedit_string)
- {
- if (fb == NULL)
- string = g_strconcat (text_tool->preedit_string, lb, NULL);
- else
- string = g_strconcat (fb, text_tool->preedit_string, lb, NULL);
- }
+ layout = gimp_text_layout_get_pango_layout (text_tool->layout);
+
+ pango_layout_get_pixel_extents (layout, &ink_extents, &logical_extents);
+ gimp_text_layout_transform_rect (text_tool->layout, &logical_extents);
+
+ if (ink_extents.x < 0)
+ *logical_off_x = -ink_extents.x;
else
- {
- string = g_strconcat (fb, lb, NULL);
- }
+ *logical_off_x = 0;
+
+ if (ink_extents.y < 0)
+ *logical_off_y = -ink_extents.y;
+ else
+ *logical_off_y = 0;
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+ gtk_text_buffer_get_insert (buffer));
+
+ string = gtk_text_buffer_get_text (buffer, &start, &cursor, FALSE);
+ cursor_index = strlen (string);
+ g_free (string);
- g_free (fb);
- g_free (lb);
+ pango_layout_index_to_pos (layout, cursor_index, cursor_rect);
+ gimp_text_layout_transform_rect (text_tool->layout, cursor_rect);
- return string;
+ cursor_rect->x = PANGO_PIXELS (cursor_rect->x) + *logical_off_x;
+ cursor_rect->y = PANGO_PIXELS (cursor_rect->y) + *logical_off_y;
+ cursor_rect->width = PANGO_PIXELS (cursor_rect->width);
+ cursor_rect->height = PANGO_PIXELS (cursor_rect->height);
}
@@ -856,8 +885,57 @@ gimp_text_tool_commit_cb (GtkIMContext *context,
}
static void
-gimp_text_tool_preedit_changed_cb (GtkIMContext *context,
- GimpTextTool *text_tool)
+gimp_text_tool_preedit_start (GtkIMContext *context,
+ GimpTextTool *text_tool)
+{
+ GimpTool *tool = GIMP_TOOL (text_tool);
+ GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
+ GtkWidget *frame;
+ PangoRectangle cursor_rect = { 0, };
+ gint unused1, unused2;
+ gint off_x, off_y;
+
+ if (text_tool->text)
+ gimp_text_tool_editor_get_cursor_rect (text_tool, &cursor_rect,
+ &unused1, &unused2);
+
+ g_object_get (text_tool, "x1", &off_x, "y1", &off_y, NULL);
+
+ text_tool->preedit_overlay = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (text_tool->preedit_overlay),
+ GTK_SHADOW_OUT);
+ gimp_display_shell_add_overlay (GIMP_DISPLAY_SHELL (shell),
+ text_tool->preedit_overlay,
+ cursor_rect.x + off_x,
+ cursor_rect.y + off_y);
+ gtk_widget_show (text_tool->preedit_overlay);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (text_tool->preedit_overlay), frame);
+ gtk_widget_show (frame);
+
+ text_tool->preedit_label = gtk_label_new (NULL);
+ gtk_misc_set_padding (GTK_MISC (text_tool->preedit_label), 2, 2);
+ gtk_container_add (GTK_CONTAINER (frame), text_tool->preedit_label);
+ gtk_widget_show (text_tool->preedit_label);
+}
+
+static void
+gimp_text_tool_preedit_end (GtkIMContext *context,
+ GimpTextTool *text_tool)
+{
+ if (text_tool->preedit_overlay)
+ {
+ gtk_widget_destroy (text_tool->preedit_overlay);
+ text_tool->preedit_overlay = NULL;
+ text_tool->preedit_label = NULL;
+ }
+}
+
+static void
+gimp_text_tool_preedit_changed (GtkIMContext *context,
+ GimpTextTool *text_tool)
{
if (text_tool->preedit_string)
g_free (text_tool->preedit_string);
@@ -866,9 +944,7 @@ gimp_text_tool_preedit_changed_cb (GtkIMContext *context,
&text_tool->preedit_string, NULL,
&text_tool->preedit_cursor);
- /* FIXME: call gimp_text_tool_update_layout() here, and make sure
- * the preedit string is *only* honored for the display, and never
- * ends up on the text object
- */
- gimp_text_tool_update_proxy (text_tool);
+ if (text_tool->preedit_label)
+ gtk_label_set_text (GTK_LABEL (text_tool->preedit_label),
+ text_tool->preedit_string);
}
diff --git a/app/tools/gimptexttool-editor.h b/app/tools/gimptexttool-editor.h
index b3d4b03..bb997a5 100644
--- a/app/tools/gimptexttool-editor.h
+++ b/app/tools/gimptexttool-editor.h
@@ -24,22 +24,25 @@
#define __GIMP_TEXT_TOOL_EDITOR_H__
-void gimp_text_tool_editor_init (GimpTextTool *text_tool);
-void gimp_text_tool_editor_finalize (GimpTextTool *text_tool);
+void gimp_text_tool_editor_init (GimpTextTool *text_tool);
+void gimp_text_tool_editor_finalize (GimpTextTool *text_tool);
-void gimp_text_tool_editor_start (GimpTextTool *text_tool);
-void gimp_text_tool_editor_halt (GimpTextTool *text_tool);
+void gimp_text_tool_editor_start (GimpTextTool *text_tool);
+void gimp_text_tool_editor_halt (GimpTextTool *text_tool);
-gboolean gimp_text_tool_editor_key_press (GimpTextTool *text_tool,
- GdkEventKey *kevent,
- GimpDisplay *display);
-gboolean gimp_text_tool_editor_key_release (GimpTextTool *text_tool,
- GdkEventKey *kevent,
- GimpDisplay *display);
+gboolean gimp_text_tool_editor_key_press (GimpTextTool *text_tool,
+ GdkEventKey *kevent,
+ GimpDisplay *display);
+gboolean gimp_text_tool_editor_key_release (GimpTextTool *text_tool,
+ GdkEventKey *kevent,
+ GimpDisplay *display);
-void gimp_text_tool_reset_im_context (GimpTextTool *text_tool);
+void gimp_text_tool_reset_im_context (GimpTextTool *text_tool);
-gchar * gimp_text_tool_editor_get_text (GimpTextTool *text_tool);
+void gimp_text_tool_editor_get_cursor_rect (GimpTextTool *text_tool,
+ PangoRectangle *cursor_rect,
+ gint *logical_off_x,
+ gint *logical_off_y);
#endif /* __GIMP_TEXT_TOOL_EDITOR_H__ */
diff --git a/app/tools/gimptexttool.c b/app/tools/gimptexttool.c
index 1e2edd5..295b353 100644
--- a/app/tools/gimptexttool.c
+++ b/app/tools/gimptexttool.c
@@ -120,9 +120,6 @@ static GimpUIManager * gimp_text_tool_get_popup (GimpTool *tool,
const gchar **ui_path);
static void gimp_text_tool_draw (GimpDrawTool *draw_tool);
-static void gimp_text_tool_draw_preedit (GimpDrawTool *draw_tool,
- gint logical_off_x,
- gint logical_off_y);
static void gimp_text_tool_draw_selection (GimpDrawTool *draw_tool,
gint logical_off_x,
gint logical_off_y);
@@ -829,11 +826,9 @@ gimp_text_tool_draw (GimpDrawTool *draw_tool)
{
GimpTextTool *text_tool = GIMP_TEXT_TOOL (draw_tool);
GtkTextBuffer *buffer = text_tool->text_buffer;
- gint logical_off_x = 0;
- gint logical_off_y = 0;
- PangoLayout *layout;
- PangoRectangle ink_extents;
- PangoRectangle logical_extents;
+ PangoRectangle cursor_rect;
+ gint logical_offset_x;
+ gint logical_offset_y;
g_object_set (text_tool,
"narrow-mode", TRUE,
@@ -846,20 +841,8 @@ gimp_text_tool_draw (GimpDrawTool *draw_tool)
! text_tool->layer->text)
return;
- /* There will be no layout if the function is called from the wrong place */
- if (! text_tool->layout)
- gimp_text_tool_update_layout (text_tool);
-
- layout = gimp_text_layout_get_pango_layout (text_tool->layout);
-
- pango_layout_get_pixel_extents (layout, &ink_extents, &logical_extents);
- gimp_text_layout_transform_rect (text_tool->layout, &logical_extents);
-
- if (ink_extents.x < 0)
- logical_off_x = -ink_extents.x;
-
- if (ink_extents.y < 0)
- logical_off_y = -ink_extents.y;
+ gimp_text_tool_editor_get_cursor_rect (text_tool, &cursor_rect,
+ &logical_offset_x, &logical_offset_y);
if (gtk_text_buffer_get_has_selection (buffer))
{
@@ -884,7 +867,8 @@ gimp_text_tool_draw (GimpDrawTool *draw_tool)
gimp_draw_tool_set_clip_rect (draw_tool, &clip_rect, FALSE);
- gimp_text_tool_draw_selection (draw_tool, logical_off_x, logical_off_y);
+ gimp_text_tool_draw_selection (draw_tool,
+ logical_offset_x, logical_offset_y);
/* Turn off clipping when done */
gimp_draw_tool_set_clip_rect (draw_tool, NULL, FALSE);
@@ -893,148 +877,18 @@ gimp_text_tool_draw (GimpDrawTool *draw_tool)
{
/* If the text buffer has no selection, draw the text cursor */
- GtkTextIter start;
- GtkTextIter cursor;
- gint cursor_index;
- PangoRectangle crect;
- gchar *string;
- gboolean overwrite_cursor;
-
- gtk_text_buffer_get_start_iter (buffer, &start);
- gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
- gtk_text_buffer_get_insert (buffer));
-
- string = gtk_text_buffer_get_text (buffer, &start, &cursor, FALSE);
-
- /* Using strlen to get the byte index, not the character offset */
- cursor_index = strlen (string);
-
- /* TODO: make cursor position itself even inside preedits! */
- if (text_tool->preedit_string)
- cursor_index += strlen (text_tool->preedit_string);
-
- g_free (string);
-
- pango_layout_index_to_pos (layout, cursor_index, &crect);
- gimp_text_layout_transform_rect (text_tool->layout, &crect);
-
- crect.x = PANGO_PIXELS (crect.x) + logical_off_x;
- crect.y = PANGO_PIXELS (crect.y) + logical_off_y;
- crect.width = PANGO_PIXELS (crect.width);
- crect.height = PANGO_PIXELS (crect.height);
-
- overwrite_cursor = text_tool->overwrite_mode && crect.width > 0;
+ gboolean overwrite = text_tool->overwrite_mode && cursor_rect.width > 0;
gimp_draw_tool_draw_text_cursor (draw_tool,
- crect.x, crect.y,
- overwrite_cursor ?
- crect.x + crect.width : crect.x,
- crect.y + crect.height,
- overwrite_cursor,
+ cursor_rect.x,
+ cursor_rect.y,
+ overwrite ?
+ cursor_rect.x + cursor_rect.width :
+ cursor_rect.x,
+ cursor_rect.y + cursor_rect.height,
+ overwrite,
TRUE);
-
- if (text_tool->preedit_string)
- gimp_text_tool_draw_preedit (draw_tool, logical_off_x, logical_off_y);
- }
-}
-
-static void
-gimp_text_tool_draw_preedit (GimpDrawTool *draw_tool,
- gint logical_off_x,
- gint logical_off_y)
-{
- GimpTextTool *text_tool = GIMP_TEXT_TOOL (draw_tool);
- PangoLayout *layout;
- PangoLayoutIter *line_iter;
- GtkTextIter cursor, start;
- gint min, max;
- gchar *string;
- gint firstline, lastline;
- gint first_x, last_x;
- gdouble first_tmp, last_tmp;
- gint i;
-
- gtk_text_buffer_get_selection_bounds (text_tool->text_buffer, &cursor, NULL);
- gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
-
- string = gtk_text_buffer_get_text (text_tool->text_buffer,
- &start, &cursor, FALSE);
- min = strlen (string);
- g_free (string);
-
- max = min + strlen (text_tool->preedit_string);
-
- layout = gimp_text_layout_get_pango_layout (text_tool->layout);
-
- pango_layout_index_to_line_x (layout, min, 0, &firstline, &first_x);
- pango_layout_index_to_line_x (layout, max, 0, &lastline, &last_x);
-
- first_tmp = first_x;
- last_tmp = last_x;
-
- gimp_text_layout_transform_distance (text_tool->layout, &first_tmp, NULL);
- gimp_text_layout_transform_distance (text_tool->layout, &last_tmp, NULL);
-
- first_x = PANGO_PIXELS (first_tmp) + logical_off_x;
- last_x = PANGO_PIXELS (last_tmp) + logical_off_x;
-
- line_iter = pango_layout_get_iter (layout);
- i = 0;
-
- do
- {
- if (i >= firstline && i <= lastline)
- {
- PangoRectangle crect;
-
- pango_layout_iter_get_line_extents (line_iter, NULL, &crect);
- pango_extents_to_pixels (&crect, NULL);
-
- gimp_text_layout_transform_rect (text_tool->layout, &crect);
-
- crect.x += logical_off_x;
- crect.y += logical_off_y;
-
- gimp_draw_tool_draw_line (draw_tool,
- crect.x, crect.y + crect.height,
- crect.x + crect.width,
- crect.y + crect.height,
- TRUE);
-
- if (i == firstline)
- {
- PangoRectangle crect2 = crect;
-
- crect2.width = first_x - crect.x;
- crect2.x = crect.x;
-
- gimp_draw_tool_draw_line (draw_tool,
- crect2.x, crect2.y + crect2.height,
- crect2.width,
- crect2.y + crect2.height,
- TRUE);
- }
-
- if (i == lastline)
- {
- PangoRectangle crect2 = crect;
-
- crect2.width = crect.x + crect.width - last_x;
- crect2.x = last_x;
-
- gimp_draw_tool_draw_line (draw_tool,
- crect2.x, crect2.y + crect2.height,
- crect2.x + crect2.width,
- crect2.y + crect2.height,
- TRUE);
- }
- }
-
- i++;
}
- while (pango_layout_iter_next_line (line_iter));
-
- pango_layout_iter_free (line_iter);
}
static void
@@ -1534,14 +1388,22 @@ gimp_text_tool_create_layer (GimpTextTool *text_tool,
}
else
{
- gchar *str = gimp_text_tool_editor_get_text (text_tool);
+ GtkTextIter start;
+ GtkTextIter end;
+ gchar *string;
+
+ gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
+ gtk_text_buffer_get_end_iter (text_tool->text_buffer, &end);
+
+ string = gtk_text_buffer_get_text (text_tool->text_buffer,
+ &start, &end, FALSE);
g_object_set (text_tool->proxy,
- "text", str,
+ "text", string,
"box-mode", GIMP_TEXT_BOX_DYNAMIC,
NULL);
- g_free (str);
+ g_free (string);
text = gimp_config_duplicate (GIMP_CONFIG (text_tool->proxy));
}
@@ -1819,7 +1681,15 @@ gimp_text_tool_update_proxy (GimpTextTool *text_tool)
{
if (text_tool->text)
{
- gchar *string = gimp_text_tool_editor_get_text (text_tool);
+ GtkTextIter start;
+ GtkTextIter end;
+ gchar *string;
+
+ gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
+ gtk_text_buffer_get_end_iter (text_tool->text_buffer, &end);
+
+ string = gtk_text_buffer_get_text (text_tool->text_buffer,
+ &start, &end, FALSE);
g_object_set (text_tool->proxy,
"text", string,
diff --git a/app/tools/gimptexttool.h b/app/tools/gimptexttool.h
index b7ea7d2..ef9c39f 100644
--- a/app/tools/gimptexttool.h
+++ b/app/tools/gimptexttool.h
@@ -73,6 +73,9 @@ struct _GimpTextTool
GtkIMContext *im_context;
gboolean needs_im_reset;
+ GtkWidget *preedit_overlay;
+ GtkWidget *preedit_label;
+
gchar *preedit_string;
gint preedit_cursor;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]