[Bf-blender-cvs] [12fdf9069ab] master: BLF: Editing Text with Combining Characters

Harley Acheson noreply at git.blender.org
Tue Sep 27 17:40:46 CEST 2022


Commit: 12fdf9069abe3cd2250a9efec6e059eb85ec59d8
Author: Harley Acheson
Date:   Tue Sep 27 08:39:24 2022 -0700
Branches: master
https://developer.blender.org/rB12fdf9069abe3cd2250a9efec6e059eb85ec59d8

BLF: Editing Text with Combining Characters

Corrections for caret insertion & movement and deletion for text
strings that include non-precomposed diacritical marks (Unicode
combining characters).

See D15659 for more details and examples.

Differential Revision: https://developer.blender.org/D15659

Reviewed by Campbell Barton

===================================================================

M	source/blender/blenfont/intern/blf_font.c
M	source/blender/blenkernel/intern/text.c
M	source/blender/blenlib/BLI_string_cursor_utf8.h
M	source/blender/blenlib/intern/string_cursor_utf8.c
M	source/blender/blenlib/tests/BLI_string_utf8_test.cc
M	source/blender/editors/curve/editfont.c

===================================================================

diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index cbf656289b5..2ee8cf088aa 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -33,6 +33,7 @@
 #include "BLI_path_util.h"
 #include "BLI_rect.h"
 #include "BLI_string.h"
+#include "BLI_string_cursor_utf8.h"
 #include "BLI_string_utf8.h"
 #include "BLI_threads.h"
 
@@ -973,9 +974,16 @@ size_t blf_str_offset_from_cursor_position(struct FontBLF *font,
       .r_offset = (size_t)-1,
   };
   blf_font_boundbox_foreach_glyph(font, str, str_len, blf_cursor_position_foreach_glyph, &data);
+
   if (data.r_offset == (size_t)-1) {
+    /* We are to the right of the string, so return position of null terminator. */
     data.r_offset = BLI_strnlen(str, str_len);
   }
+  else if (BLI_str_utf8_char_width(&str[data.r_offset]) < 1) {
+    /* This is a combining character (or invalid), so move to previous visible valid char. */
+    BLI_str_cursor_step_prev_utf8(str, str_len, (int *)&data.r_offset);
+  }
+
   return data.r_offset;
 }
 
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index c32ab64c478..1a0c0716fcd 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -896,8 +896,7 @@ void txt_move_left(Text *text, const bool sel)
       (*charp) -= tabsize;
     }
     else {
-      const char *prev = BLI_str_find_prev_char_utf8((*linep)->line + *charp, (*linep)->line);
-      *charp = prev - (*linep)->line;
+      BLI_str_cursor_step_prev_utf8((*linep)->line, (*linep)->len, charp);
     }
   }
 
@@ -941,7 +940,7 @@ void txt_move_right(Text *text, const bool sel)
       (*charp) += tabsize;
     }
     else {
-      (*charp) += BLI_str_utf8_size((*linep)->line + *charp);
+      BLI_str_cursor_step_next_utf8((*linep)->line, (*linep)->len, charp);
     }
   }
 
@@ -1757,8 +1756,6 @@ void txt_duplicate_line(Text *text)
 
 void txt_delete_char(Text *text)
 {
-  uint c = '\n';
-
   if (!text->curl) {
     return;
   }
@@ -1778,10 +1775,9 @@ void txt_delete_char(Text *text)
     }
   }
   else { /* Just deleting a char */
-    size_t c_len = text->curc;
-    c = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &c_len);
-    c_len -= text->curc;
-    UNUSED_VARS(c);
+    int pos = text->curc;
+    BLI_str_cursor_step_next_utf8(text->curl->line, text->curl->len, &pos);
+    size_t c_len = pos - text->curc;
 
     memmove(text->curl->line + text->curc,
             text->curl->line + text->curc + c_len,
@@ -1805,8 +1801,6 @@ void txt_delete_word(Text *text)
 
 void txt_backspace_char(Text *text)
 {
-  uint c = '\n';
-
   if (!text->curl) {
     return;
   }
@@ -1828,13 +1822,9 @@ void txt_backspace_char(Text *text)
     txt_pop_sel(text);
   }
   else { /* Just backspacing a char */
-    const char *prev = BLI_str_find_prev_char_utf8(text->curl->line + text->curc,
-                                                   text->curl->line);
-    size_t c_len = prev - text->curl->line;
-    c = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &c_len);
-    c_len -= prev - text->curl->line;
-
-    UNUSED_VARS(c);
+    int pos = text->curc;
+    BLI_str_cursor_step_prev_utf8(text->curl->line, text->curl->len, &pos);
+    size_t c_len = text->curc - pos;
 
     /* source and destination overlap, don't use memcpy() */
     memmove(text->curl->line + text->curc - c_len,
diff --git a/source/blender/blenlib/BLI_string_cursor_utf8.h b/source/blender/blenlib/BLI_string_cursor_utf8.h
index 70ba5da8e5a..9c0589b230a 100644
--- a/source/blender/blenlib/BLI_string_cursor_utf8.h
+++ b/source/blender/blenlib/BLI_string_cursor_utf8.h
@@ -25,6 +25,9 @@ typedef enum eStrCursorJumpDirection {
 bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos);
 bool BLI_str_cursor_step_prev_utf8(const char *str, size_t maxlen, int *pos);
 
+bool BLI_str_cursor_step_next_utf32(const char32_t *str, size_t maxlen, int *pos);
+bool BLI_str_cursor_step_prev_utf32(const char32_t *str, size_t maxlen, int *pos);
+
 void BLI_str_cursor_step_utf8(const char *str,
                               size_t maxlen,
                               int *pos,
diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c
index 6b7151be969..2405b134428 100644
--- a/source/blender/blenlib/intern/string_cursor_utf8.c
+++ b/source/blender/blenlib/intern/string_cursor_utf8.c
@@ -96,27 +96,35 @@ static eStrCursorDelimType cursor_delim_type_utf8(const char *ch_utf8,
   return cursor_delim_type_unicode(uch);
 }
 
+/* Keep in sync with BLI_str_cursor_step_next_utf32. */
 bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos)
 {
+  if ((*pos) >= (int)maxlen) {
+    return false;
+  }
   const char *str_end = str + (maxlen + 1);
   const char *str_pos = str + (*pos);
-  const char *str_next = BLI_str_find_next_char_utf8(str_pos, str_end);
-  if (str_next != str_end) {
-    (*pos) += (str_next - str_pos);
-    if ((*pos) > (int)maxlen) {
-      (*pos) = (int)maxlen;
-    }
-    return true;
+  const char *str_next = str_pos;
+  do {
+    str_next = BLI_str_find_next_char_utf8(str_next, str_end);
+  } while (str_next < str_end && str_next[0] != 0 && BLI_str_utf8_char_width(str_next) < 1);
+  (*pos) += (str_next - str_pos);
+  if ((*pos) > (int)maxlen) {
+    (*pos) = (int)maxlen;
   }
 
-  return false;
+  return true;
 }
 
-bool BLI_str_cursor_step_prev_utf8(const char *str, size_t UNUSED(maxlen), int *pos)
+/* Keep in sync with BLI_str_cursor_step_prev_utf32. */
+bool BLI_str_cursor_step_prev_utf8(const char *str, size_t maxlen, int *pos)
 {
-  if ((*pos) > 0) {
+  if ((*pos) > 0 && (*pos) <= maxlen) {
     const char *str_pos = str + (*pos);
-    const char *str_prev = BLI_str_find_prev_char_utf8(str_pos, str);
+    const char *str_prev = str_pos;
+    do {
+      str_prev = BLI_str_find_prev_char_utf8(str_prev, str);
+    } while (str_prev > str && BLI_str_utf8_char_width(str_prev) == 0);
     (*pos) -= (str_pos - str_prev);
     return true;
   }
@@ -202,26 +210,29 @@ void BLI_str_cursor_step_utf8(const char *str,
   }
 }
 
-/* UTF32 version of BLI_str_cursor_step_utf8 (keep in sync!)
- * less complex since it doesn't need to do multi-byte stepping.
- */
-
-/* Helper functions so we can match #BLI_str_cursor_step_utf8. */
-static bool cursor_step_next_utf32(const char32_t *UNUSED(str), size_t maxlen, int *pos)
+/* Keep in sync with BLI_str_cursor_step_next_utf8. */
+bool BLI_str_cursor_step_next_utf32(const char32_t *str, size_t maxlen, int *pos)
 {
   if ((*pos) >= (int)maxlen) {
     return false;
   }
-  (*pos)++;
+  do {
+    (*pos)++;
+  } while (*pos < (int)maxlen && str[*pos] != 0 && BLI_wcwidth(str[*pos]) == 0);
+
   return true;
 }
 
-static bool cursor_step_prev_utf32(const char32_t *UNUSED(str), size_t UNUSED(maxlen), int *pos)
+/* Keep in sync with BLI_str_cursor_step_prev_utf8. */
+bool BLI_str_cursor_step_prev_utf32(const char32_t *str, size_t UNUSED(maxlen), int *pos)
 {
   if ((*pos) <= 0) {
     return false;
   }
-  (*pos)--;
+  do {
+    (*pos)--;
+  } while (*pos > 0 && BLI_wcwidth(str[*pos]) == 0);
+
   return true;
 }
 
@@ -236,7 +247,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
 
   if (direction == STRCUR_DIR_NEXT) {
     if (use_init_step) {
-      cursor_step_next_utf32(str, maxlen, pos);
+      BLI_str_cursor_step_next_utf32(str, maxlen, pos);
     }
     else {
       BLI_assert(jump == STRCUR_JUMP_DELIM);
@@ -250,7 +261,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
        * look at function cursor_delim_type_unicode() for complete
        * list of special character, ctr -> */
       while ((*pos) < maxlen) {
-        if (cursor_step_next_utf32(str, maxlen, pos)) {
+        if (BLI_str_cursor_step_next_utf32(str, maxlen, pos)) {
           if ((jump != STRCUR_JUMP_ALL) &&
               (delim_type != cursor_delim_type_unicode((uint)str[*pos]))) {
             break;
@@ -264,7 +275,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
   }
   else if (direction == STRCUR_DIR_PREV) {
     if (use_init_step) {
-      cursor_step_prev_utf32(str, maxlen, pos);
+      BLI_str_cursor_step_prev_utf32(str, maxlen, pos);
     }
     else {
       BLI_assert(jump == STRCUR_JUMP_DELIM);
@@ -279,7 +290,7 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
        * list of special character, ctr -> */
       while ((*pos) > 0) {
         const int pos_prev = *pos;
-        if (cursor_step_prev_utf32(str, maxlen, pos)) {
+        if (BLI_str_cursor_step_prev_utf32(str, maxlen, pos)) {
           if ((jump != STRCUR_JUMP_ALL) &&
               (delim_type != cursor_delim_type_unicode((uint)str[*pos]))) {
             /* left only: compensate for index/change in direction */
diff --git a/source/blender/blenlib/tests/BLI_string_utf8_test.cc b/source/blender/blenlib/tests/BLI_string_utf8_test.cc
index 7179eef5adb..f0c34350e1b 100644
--- a/source/blender/blenlib/tests/BLI_string_utf8_test.cc
+++ b/source/blender/blenlib/tests/BLI_string_utf8_test.cc
@@ -4,6 +4,7 @@
 
 #include "BLI_rand.h"
 #include "BLI_string.h"
+#include "BLI_string_cursor_utf8.h"
 #include "BLI_string_utf8.h"
 #include "BLI_utildefines.h"
 
@@ -393,3 +394,485 @@ TEST(string, Utf8AsUnicodeStep)
 }
 
 /** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_empty
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32Empty)
+{
+  const char32_t empty[] = U"";
+  const size_t len = 0;
+  int pos = 0;
+  EXPECT_FALSE(BLI_str_cursor_step_next_utf32(empty, len, &pos));
+  pos = 1;
+  EXPECT_FALSE(BLI_str_cursor_step_next_utf32(empty, len, &pos));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Test #BLI_str_cursor_step_next_utf32_single
+ * \{ */
+
+TEST(string, StrCursorStepNextUtf32Single)
+
+{
+  const char32_t single[] = U"0";
+  const size_t len = 1;
+  int pos = 0;
+  EXPECT_TRUE(BLI_str_cursor_step_next_utf32(single, len, &pos) && pos == 1);
+  EXPECT_FALSE(BLI_str_cursor_step_next_utf32(single, len, &pos));
+}
+
+/* -----------------------

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list