gpui: Fix text ellipsis appearing even the flex element has space (#17149)

Jason Lee created

Release Notes:

- N/A

There was a calculation bug before. When we added `text_ellipsis` to the
flex element, it would always show ellipsis no matter how long it was.

Actually we can't use `flex` and `text_ellipsis` at same time, the CSS
also not support this. But this bug will let user confuse.

### Before

<img width="731" alt="image"
src="https://github.com/user-attachments/assets/b7d60017-6785-45f5-8b40-dd5efa154a1e">

### After

<img width="521" alt="image"
src="https://github.com/user-attachments/assets/a3117793-284e-48d4-8c15-059fe61abe60">

Change summary

crates/gpui/examples/text_wrapper.rs        | 30 +++++++++++++++++++++++
crates/gpui/src/text_system/line_wrapper.rs | 17 ++++++++++--
2 files changed, 44 insertions(+), 3 deletions(-)

Detailed changes

crates/gpui/examples/text_wrapper.rs 🔗

@@ -13,6 +13,36 @@ impl Render for HelloWorld {
             .p_2()
             .gap_2()
             .bg(gpui::white())
+            .child(
+                div()
+                    .flex()
+                    .flex_row()
+                    .gap_2()
+                    .child(
+                        div()
+                            .flex()
+                            .border_1()
+                            .border_color(gpui::red())
+                            .text_ellipsis()
+                            .child("longer text in flex 1"),
+                    )
+                    .child(
+                        div()
+                            .flex()
+                            .border_1()
+                            .border_color(gpui::red())
+                            .text_ellipsis()
+                            .child("short flex"),
+                    )
+                    .child(
+                        div()
+                            .overflow_hidden()
+                            .border_1()
+                            .border_color(gpui::red())
+                            .text_ellipsis()
+                            .child("A short text in normal div"),
+                    ),
+            )
             .child(
                 div()
                     .text_xl()

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

@@ -106,18 +106,29 @@ impl LineWrapper {
         ellipsis: Option<&str>,
     ) -> SharedString {
         let mut width = px(0.);
+        let mut ellipsis_width = px(0.);
         if let Some(ellipsis) = ellipsis {
             for c in ellipsis.chars() {
-                width += self.width_for_char(c);
+                ellipsis_width += self.width_for_char(c);
             }
         }
 
         let mut char_indices = line.char_indices();
+        let mut truncate_ix = 0;
         for (ix, c) in char_indices {
+            if width + ellipsis_width <= truncate_width {
+                truncate_ix = ix;
+            }
+
             let char_width = self.width_for_char(c);
             width += char_width;
-            if width > truncate_width {
-                return SharedString::from(format!("{}{}", &line[..ix], ellipsis.unwrap_or("")));
+
+            if width.floor() > truncate_width {
+                return SharedString::from(format!(
+                    "{}{}",
+                    &line[..truncate_ix],
+                    ellipsis.unwrap_or("")
+                ));
             }
         }