[gtk: 1/2] textchildanchor: allow to specify replacement character




commit c517e945de399488b5c7577a25617a62bc2cb92a
Author: Georg Vienna <georg vienna himbarsoft com>
Date:   Fri Dec 3 16:02:48 2021 +0000

    textchildanchor: allow to specify replacement character

 demos/gtk-demo/hypertext.c | 10 +++++--
 gtk/gtktextbtree.c         | 35 +++++++++++++++++++++--
 gtk/gtktextchild.c         | 33 +++++++++++++++------
 gtk/gtktextchild.h         | 14 +++++----
 gtk/gtktextiter.c          |  4 +++
 gtk/gtktextlayout.c        |  2 +-
 testsuite/gtk/textbuffer.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 150 insertions(+), 19 deletions(-)
---
diff --git a/demos/gtk-demo/hypertext.c b/demos/gtk-demo/hypertext.c
index b38f474c33..e47f691f53 100644
--- a/demos/gtk-demo/hypertext.c
+++ b/demos/gtk-demo/hypertext.c
@@ -7,7 +7,8 @@
  * shows.
  *
  * We also demonstrate adding other things to a text view, such as
- * clickable icons.
+ * clickable icons and widgets which can also replace a character
+ * (try copying the ghost text).
  */
 
 #include <gtk/gtk.h>
@@ -113,7 +114,12 @@ show_page (GtkTextView *text_view,
       gtk_level_bar_set_value (GTK_LEVEL_BAR (child), 50);
       gtk_widget_set_size_request (child, 100, -1);
       gtk_text_view_add_child_at_anchor (text_view, child, anchor);
-      gtk_text_buffer_insert (buffer, &iter, ".", -1);
+      gtk_text_buffer_insert (buffer, &iter, " and labels with ", -1);
+      anchor = gtk_text_child_anchor_new_with_replacement ("👻");
+      gtk_text_buffer_insert_child_anchor (buffer, &iter, anchor);
+      child = gtk_label_new ("ghost");
+      gtk_text_view_add_child_at_anchor (text_view, child, anchor);
+      gtk_text_buffer_insert (buffer, &iter, " text.", -1);
     }
   else if (page == 2)
     {
diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c
index 91ab3351a8..d11d4300c7 100644
--- a/gtk/gtktextbtree.c
+++ b/gtk/gtktextbtree.c
@@ -2362,8 +2362,7 @@ copy_segment (GString *string,
 
       /* printf ("  :%s\n", string->str); */
     }
-  else if (seg->type == &gtk_text_paintable_type ||
-           seg->type == &gtk_text_child_type)
+  else if (seg->type == &gtk_text_paintable_type)
     {
       gboolean copy = TRUE;
 
@@ -2382,7 +2381,27 @@ copy_segment (GString *string,
           g_string_append_len (string,
                                _gtk_text_unknown_char_utf8,
                                GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN);
+        }
+    }
+  else if (seg->type == &gtk_text_child_type)
+    {
+      gboolean copy = TRUE;
+      if (!include_nonchars &&
+          g_strcmp0 (_gtk_text_unknown_char_utf8, seg->body.child.obj->chars) == 0)
+        {
+          copy = FALSE;
+        }
+      else if (!include_hidden &&
+               _gtk_text_btree_char_is_invisible (start))
+        {
+          copy = FALSE;
+        }
 
+      if (copy)
+        {
+          g_string_append_len (string,
+                               seg->body.child.obj->chars,
+                               seg->byte_count);
         }
     }
 }
@@ -7121,6 +7140,12 @@ _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
           printf ("%s chars '%s'...\n", spaces, str);
           g_free (str);
         }
+      else if (seg->type == &gtk_text_child_type)
+        {
+          char *str = g_strndup (seg->body.child.obj->chars, seg->byte_count);
+          printf ("%s child '%s'...\n", spaces, str);
+          g_free (str);
+        }
       else if (seg->type == &gtk_text_right_mark_type)
         {
           printf ("%s right mark '%s' visible: %d\n",
@@ -7223,6 +7248,12 @@ _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
       printf ("       '%s'\n", str);
       g_free (str);
     }
+  else if (seg->type == &gtk_text_child_type)
+    {
+      char *str = g_strndup (seg->body.child.obj->chars, seg->byte_count);
+      printf ("       '%s'\n", str);
+      g_free (str);
+    }
   else if (seg->type == &gtk_text_right_mark_type)
     {
       printf ("       right mark '%s' visible: %d not_deleteable: %d\n",
diff --git a/gtk/gtktextchild.c b/gtk/gtktextchild.c
index f1716efb75..2976550301 100644
--- a/gtk/gtktextchild.c
+++ b/gtk/gtktextchild.c
@@ -267,9 +267,6 @@ child_segment_check_func (GtkTextLineSegment *seg,
   if (seg->next == NULL)
     g_error ("child segment is the last segment in a line");
 
-  if (seg->byte_count != GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN)
-    g_error ("child segment has byte count of %d", seg->byte_count);
-
   if (seg->char_count != 1)
     g_error ("child segment has char count of %d", seg->char_count);
 }
@@ -301,11 +298,8 @@ _gtk_widget_segment_new (GtkTextChildAnchor *anchor)
 
   seg->next = NULL;
 
-  /* We convert to the 0xFFFC "unknown character",
-   * a 3-byte sequence in UTF-8.
-   */
-  seg->byte_count = GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN;
-  seg->char_count = 1;
+  seg->byte_count = strlen (anchor->chars);
+  seg->char_count = g_utf8_strlen (anchor->chars, seg->byte_count);
 
   seg->body.child.obj = anchor;
   seg->body.child.obj->segment = seg;
@@ -410,7 +404,28 @@ gtk_text_child_anchor_class_init (GtkTextChildAnchorClass *klass)
 GtkTextChildAnchor*
 gtk_text_child_anchor_new (void)
 {
-  return g_object_new (GTK_TYPE_TEXT_CHILD_ANCHOR, NULL);
+  return gtk_text_child_anchor_new_with_replacement (_gtk_text_unknown_char_utf8);
+}
+
+/**
+ * gtk_text_child_anchor_new_with_replacement:
+ *
+ * Creates a new `GtkTextChildAnchor` with the given replacement character.
+ *
+ * Usually you would then insert it into a `GtkTextBuffer` with
+ * [method@Gtk.TextBuffer.insert_child_anchor].
+ *
+ * Returns: a new `GtkTextChildAnchor`
+ **/
+GtkTextChildAnchor *
+gtk_text_child_anchor_new_with_replacement (const char *replacement_character)
+{
+  /* only a single character can be set as replacement */
+  g_return_val_if_fail (g_utf8_strlen (replacement_character, -1) == 1, NULL);
+
+  GtkTextChildAnchor *anchor = g_object_new (GTK_TYPE_TEXT_CHILD_ANCHOR, NULL);
+  anchor->chars = g_strdup (replacement_character);
+  return anchor;
 }
 
 static void
diff --git a/gtk/gtktextchild.h b/gtk/gtktextchild.h
index 65432262a1..31e8324c59 100644
--- a/gtk/gtktextchild.h
+++ b/gtk/gtktextchild.h
@@ -60,6 +60,7 @@ struct _GtkTextChildAnchor
 
   /*< private >*/
   gpointer segment;
+  char *chars; /* replacement character */
 };
 
 struct _GtkTextChildAnchorClass
@@ -74,16 +75,19 @@ struct _GtkTextChildAnchorClass
 };
 
 GDK_AVAILABLE_IN_ALL
-GType               gtk_text_child_anchor_get_type    (void) G_GNUC_CONST;
+GType gtk_text_child_anchor_get_type (void) G_GNUC_CONST;
 
 GDK_AVAILABLE_IN_ALL
-GtkTextChildAnchor* gtk_text_child_anchor_new         (void);
+GtkTextChildAnchor *gtk_text_child_anchor_new (void);
+
+GDK_AVAILABLE_IN_4_6
+GtkTextChildAnchor *gtk_text_child_anchor_new_with_replacement (const char *character);
 
 GDK_AVAILABLE_IN_ALL
-GtkWidget **        gtk_text_child_anchor_get_widgets (GtkTextChildAnchor *anchor,
-                                                       guint              *out_len);
+GtkWidget **gtk_text_child_anchor_get_widgets (GtkTextChildAnchor *anchor,
+                                               guint *out_len);
 GDK_AVAILABLE_IN_ALL
-gboolean            gtk_text_child_anchor_get_deleted (GtkTextChildAnchor *anchor);
+gboolean gtk_text_child_anchor_get_deleted (GtkTextChildAnchor *anchor);
 
 G_END_DECLS
 
diff --git a/gtk/gtktextiter.c b/gtk/gtktextiter.c
index 7157233641..a9e79ea51f 100644
--- a/gtk/gtktextiter.c
+++ b/gtk/gtktextiter.c
@@ -892,6 +892,10 @@ gtk_text_iter_get_char (const GtkTextIter *iter)
       return g_utf8_get_char (real->segment->body.chars +
                               real->segment_byte_offset);
     }
+  else if (real->segment->type == &gtk_text_child_type)
+    {
+      return g_utf8_get_char (real->segment->body.child.obj->chars);
+    }
   else
     {
       /* Unicode "unknown character" 0xFFFC */
diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c
index 1873b03d74..b57cd65e50 100644
--- a/gtk/gtktextlayout.c
+++ b/gtk/gtktextlayout.c
@@ -2470,7 +2470,7 @@ gtk_text_layout_create_display (GtkTextLayout *layout,
                                      size_only, FALSE);
                   add_child_attrs (layout, display, style,
                                    seg, attrs, layout_byte_offset);
-                  memcpy (text + layout_byte_offset, _gtk_text_unknown_char_utf8,
+                  memcpy (text + layout_byte_offset, seg->body.child.obj->chars,
                           seg->byte_count);
                   layout_byte_offset += seg->byte_count;
                   buffer_byte_offset += seg->byte_count;
diff --git a/testsuite/gtk/textbuffer.c b/testsuite/gtk/textbuffer.c
index 3ac7643a6a..2dcb0b5fd7 100644
--- a/testsuite/gtk/textbuffer.c
+++ b/testsuite/gtk/textbuffer.c
@@ -1581,6 +1581,75 @@ test_get_iter (void)
   g_object_unref (buffer);
 }
 
+static void
+test_iter_with_anchor (void)
+{
+  GtkTextView *text_view;
+  GtkTextBuffer *buffer;
+  GtkTextIter iter;
+  GtkTextChildAnchor *anchor;
+  GtkWidget *child;
+
+  text_view = GTK_TEXT_VIEW (gtk_text_view_new ());
+  buffer = gtk_text_view_get_buffer (text_view);
+
+  gtk_text_buffer_set_text (buffer, "ab\ncd\r\nef", -1);
+  g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 1));
+  anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
+  child = gtk_label_new ("text");
+  gtk_text_view_add_child_at_anchor (text_view, child, anchor);
+  g_object_unref (child);
+
+  gtk_text_buffer_get_iter_at_child_anchor (buffer, &iter, anchor);
+  g_assert_cmpint (gtk_text_iter_get_char (&iter), ==, GTK_TEXT_UNKNOWN_CHAR);
+
+  g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 1));
+  /* ß takes 2 bytes in UTF-8 */
+  anchor = gtk_text_child_anchor_new_with_replacement ("ß");
+  gtk_text_buffer_insert_child_anchor (buffer, &iter, anchor);
+  child = gtk_label_new ("text");
+  gtk_text_view_add_child_at_anchor (text_view, child, anchor);
+
+  gtk_text_buffer_get_iter_at_child_anchor (buffer, &iter, anchor);
+  g_assert_cmpint (gtk_text_iter_get_char (&iter), ==, 223);
+
+  g_object_unref (child);
+  g_object_ref_sink (text_view);
+}
+
+static void
+test_get_text_with_anchor (void)
+{
+  GtkTextView *text_view;
+  GtkTextBuffer *buffer;
+  GtkTextIter iter, start, end;
+  GtkTextChildAnchor *anchor;
+  GtkWidget *child;
+
+  text_view = GTK_TEXT_VIEW (gtk_text_view_new ());
+  buffer = gtk_text_view_get_buffer (text_view);
+
+  gtk_text_buffer_set_text (buffer, "ab\ncd\r\nef", -1);
+  g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 1));
+  anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
+  child = gtk_label_new ("text");
+  gtk_text_view_add_child_at_anchor (text_view, child, anchor);
+  g_object_unref (child);
+
+  g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 1));
+  /* ß takes 2 bytes in UTF-8 */
+  anchor = gtk_text_child_anchor_new_with_replacement ("ß");
+  gtk_text_buffer_insert_child_anchor (buffer, &iter, anchor);
+  child = gtk_label_new ("text");
+  gtk_text_view_add_child_at_anchor (text_view, child, anchor);
+
+  gtk_text_buffer_get_bounds (buffer, &start, &end);
+  g_assert_cmpstr (gtk_text_buffer_get_text (buffer, &start, &end, FALSE), ==, "ab\ncßd\r\nef");
+
+  g_object_unref (child);
+  g_object_ref_sink (text_view);
+}
+
 /* Check that basic undo works */
 static void
 test_undo0 (void)
@@ -1768,6 +1837,8 @@ main (int argc, char** argv)
   g_test_add_func ("/TextBuffer/Tag", test_tag);
   g_test_add_func ("/TextBuffer/Clipboard", test_clipboard);
   g_test_add_func ("/TextBuffer/Get iter", test_get_iter);
+  g_test_add_func ("/TextBuffer/Iter with anchor", test_iter_with_anchor);
+  g_test_add_func ("/TextBuffer/Get text with anchor", test_get_text_with_anchor);
   g_test_add_func ("/TextBuffer/Undo 0", test_undo0);
   g_test_add_func ("/TextBuffer/Undo 1", test_undo1);
   g_test_add_func ("/TextBuffer/Undo 2", test_undo2);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]