summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-12-03 16:02:49 +0000
committerMatthias Clasen <mclasen@redhat.com>2021-12-03 16:02:49 +0000
commitd2bda8ea77858c8e6ad1053b38394a0675bb2f84 (patch)
treed6ef3d3db2e028476a36bd154c582efdf7221684
parente3a1a2e0c6b36f29173f44e4ed914b2cc9a13a98 (diff)
parentc517e945de399488b5c7577a25617a62bc2cb92a (diff)
downloadgtk+-d2bda8ea77858c8e6ad1053b38394a0675bb2f84.tar.gz
Merge branch 'text-anchor-replacement-char' into 'main'
textchildanchor: allow to specify replacement character See merge request GNOME/gtk!4213
-rw-r--r--demos/gtk-demo/hypertext.c10
-rw-r--r--gtk/gtktextbtree.c35
-rw-r--r--gtk/gtktextchild.c33
-rw-r--r--gtk/gtktextchild.h14
-rw-r--r--gtk/gtktextiter.c4
-rw-r--r--gtk/gtktextlayout.c2
-rw-r--r--testsuite/gtk/textbuffer.c71
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);