[libadwaita/wip/exalm/avatar] avatar: Fix initial extraction




commit eed120da7a25b7764ccbb396cbf2ade5e64c2eda
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Wed Oct 6 23:44:54 2021 +0500

    avatar: Fix initial extraction
    
    Rewrite it using Pango - correctly handle clusters, word boundaries and
    hopefully RTL.
    
    The returned string can be empty now if there are no actual words there,
    so account for that everywhere.

 src/adw-avatar.c | 110 ++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 88 insertions(+), 22 deletions(-)
---
diff --git a/src/adw-avatar.c b/src/adw-avatar.c
index 34ece640..6afe7f64 100644
--- a/src/adw-avatar.c
+++ b/src/adw-avatar.c
@@ -72,30 +72,95 @@ enum {
 static GParamSpec *props[PROP_LAST_PROP];
 
 static char *
-extract_initials_from_text (const char *text)
+extract_initials_from_text (AdwAvatar  *self,
+                            const char *text)
 {
+  g_autofree char *normalized = g_utf8_normalize (text, -1, G_NORMALIZE_DEFAULT_COMPOSE);
+  g_autofree char *upper = g_utf8_strup (normalized, -1);
+  PangoContext *ctx = gtk_widget_get_pango_context (GTK_WIDGET (self->label));
+  PangoLayout *layout = pango_layout_new (ctx);
+  const PangoLogAttr *attrs = NULL;
   GString *initials;
-  g_autofree char *p = g_utf8_strup (text, -1);
-  g_autofree char *normalized = g_utf8_normalize (g_strstrip (p), -1, G_NORMALIZE_DEFAULT_COMPOSE);
-  gunichar unichar;
-  char *q = NULL;
+  PangoLayoutIter *iter, *first_word = NULL, *last_word = NULL;
+  int i, n_attrs, from, to;
 
-  if (normalized == NULL)
-    return NULL;
+  pango_layout_set_text (layout, upper, -1);
+  pango_layout_set_single_paragraph_mode (layout, TRUE);
+  iter = pango_layout_get_iter (layout);
+  attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
+
+  for (i = 0; i < n_attrs; i++) {
+    PangoDirection direction_before, direction_after;
+    PangoLayoutIter *iter2;
+
+    if (!attrs[i].is_word_start) {
+      pango_layout_iter_next_char (iter);
+
+      continue;
+    }
+
+    iter2 = pango_layout_iter_copy (iter);
+    from = pango_layout_iter_get_index (iter2);
+    pango_layout_iter_next_char (iter2);
+    to = pango_layout_iter_get_index (iter2);
+    pango_layout_iter_free (iter2);
+
+    if (from == to) {
+      pango_layout_iter_next_char (iter);
+
+      continue;
+    }
+
+    direction_before = pango_layout_get_direction (layout, from);
+    direction_after = pango_layout_get_direction (layout, to);
+
+    if (direction_before != direction_after) {
+      pango_layout_iter_next_char (iter);
+
+      continue;
+    }
+
+    if (!first_word)
+      first_word = pango_layout_iter_copy (iter);
+
+    if (last_word)
+      pango_layout_iter_free (last_word);
+
+    last_word = pango_layout_iter_copy (iter);
+
+    pango_layout_iter_next_char (iter);
+  }
+
+  if (!first_word)
+    return g_strdup ("");
 
   initials = g_string_new ("");
 
-  unichar = g_utf8_get_char (normalized);
-  g_string_append_unichar (initials, unichar);
+  from = pango_layout_iter_get_index (first_word);
+  pango_layout_iter_next_cluster (first_word);
+  to = pango_layout_iter_get_index (first_word);
+
+  if (pango_layout_get_direction (layout, from) == PANGO_DIRECTION_RTL)
+    g_string_prepend_len (initials, upper + to, from - to);
+  else
+    g_string_append_len (initials, upper + from, to - from);
 
-  q = g_utf8_strrchr (normalized, -1, ' ');
-  if (q != NULL && g_utf8_next_char (q) != NULL) {
-    q = g_utf8_next_char (q);
+  if (from != pango_layout_iter_get_index (last_word)) {
+    from = pango_layout_iter_get_index (last_word);
+    pango_layout_iter_next_cluster (last_word);
+    to = pango_layout_iter_get_index (last_word);
 
-    unichar = g_utf8_get_char (q);
-    g_string_append_unichar (initials, unichar);
+    if (pango_layout_get_direction (layout, from) == PANGO_DIRECTION_RTL)
+      g_string_prepend_len (initials, upper + to, from - to);
+    else
+      g_string_append_len (initials, upper + from, to - from);
   }
 
+  pango_layout_iter_free (iter);
+  pango_layout_iter_free (first_word);
+  pango_layout_iter_free (last_word);
+  g_object_unref (layout);
+
   return g_string_free (initials, FALSE);
 }
 
@@ -103,7 +168,8 @@ static void
 update_visibility (AdwAvatar *self)
 {
   gboolean has_custom_image = gtk_image_get_paintable (self->custom_image) != NULL;
-  gboolean has_initials = self->show_initials && self->text && strlen (self->text);
+  const char *initials = gtk_label_get_label (self->label);
+  gboolean has_initials = self->show_initials && initials && initials[0];
 
   gtk_widget_set_visible (GTK_WIDGET (self->label), !has_custom_image && has_initials);
   gtk_widget_set_visible (GTK_WIDGET (self->icon), !has_custom_image && !has_initials);
@@ -138,14 +204,17 @@ update_initials (AdwAvatar *self)
   g_autofree char *initials = NULL;
 
   if (gtk_image_get_paintable (self->custom_image) != NULL ||
-      !self->show_initials ||
-      self->text == NULL ||
-      strlen (self->text) == 0)
+      !self->show_initials) {
+    update_visibility (self);
+
     return;
+  }
 
-  initials = extract_initials_from_text (self->text);
+  initials = extract_initials_from_text (self, self->text);
 
   gtk_label_set_label (self->label, initials);
+
+  update_visibility (self);
 }
 
 static void
@@ -408,7 +477,6 @@ adw_avatar_init (AdwAvatar *self)
   update_initials (self);
   update_font_size (self);
   update_icon (self);
-  update_visibility (self);
 
   g_signal_connect (self, "notify::root", G_CALLBACK (update_font_size), NULL);
 }
@@ -527,7 +595,6 @@ adw_avatar_set_text (AdwAvatar  *self,
 
   update_initials (self);
   update_font_size (self);
-  update_visibility (self);
 
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TEXT]);
 }
@@ -572,7 +639,6 @@ adw_avatar_set_show_initials (AdwAvatar *self,
 
   update_initials (self);
   update_font_size (self);
-  update_visibility (self);
 
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SHOW_INITIALS]);
 }


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