gpui: Fix CJK line wrap for GPUI text render (#17737)

Jason Lee created

Release Notes:

- N/A

This changes is going to let GPUI render correct text wrapping for CJK
characters. We was done this in PR #11296 for Editor, but this is also
need support for other text renders.

| Before | After |
| --- | --- |
| <img width="488" alt="SCR-20240912-jtvo"
src="https://github.com/user-attachments/assets/d061669c-62ab-4a7e-a724-2df84815d1ed">
| <img width="438" alt="image"
src="https://github.com/user-attachments/assets/ec27fd80-69db-48b6-8ade-694cd65d1843">
|

Change summary

crates/gpui/examples/text_wrapper.rs       |  3 ++-
crates/gpui/src/text_system/line_layout.rs | 17 ++++++++++++++---
2 files changed, 16 insertions(+), 4 deletions(-)

Detailed changes

crates/gpui/examples/text_wrapper.rs 🔗

@@ -4,7 +4,7 @@ struct HelloWorld {}
 
 impl Render for HelloWorld {
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let text = "The longest word in any of the major English language 以及中文的测试 dictionaries is pneumonoultramicroscopicsilicovolcanoconiosis, a word that refers to a lung disease contracted from the inhalation of very fine silica particles, specifically from a volcano; medically, it is the same as silicosis.";
+        let text = "The longest word 你好世界这段是中文,こんにちはこの段落は日本語です in any of the major English language dictionaries is pneumonoultramicroscopicsilicovolcanoconiosis, a word that refers to a lung disease contracted from the inhalation of very fine silica particles, specifically from a volcano; medically, it is the same as silicosis.";
         div()
             .id("page")
             .size_full()
@@ -40,6 +40,7 @@ impl Render for HelloWorld {
                             .border_1()
                             .border_color(gpui::red())
                             .text_ellipsis()
+                            .w_full()
                             .child("A short text in normal div"),
                     ),
             )

crates/gpui/src/text_system/line_layout.rs 🔗

@@ -9,6 +9,8 @@ use std::{
     sync::Arc,
 };
 
+use super::LineWrapper;
+
 /// A laid out and styled line of text
 #[derive(Default, Debug)]
 pub struct LineLayout {
@@ -152,9 +154,18 @@ impl LineLayout {
                 continue;
             }
 
-            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
-                last_candidate_ix = Some(boundary);
-                last_candidate_x = x;
+            // Here is very similar to `LineWrapper::wrap_line` to determine text wrapping,
+            // but there are some differences, so we have to duplicate the code here.
+            if LineWrapper::is_word_char(ch) {
+                if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
+                    last_candidate_ix = Some(boundary);
+                    last_candidate_x = x;
+                }
+            } else {
+                if ch != ' ' && first_non_whitespace_ix.is_some() {
+                    last_candidate_ix = Some(boundary);
+                    last_candidate_x = x;
+                }
             }
 
             if ch != ' ' && first_non_whitespace_ix.is_none() {