gpui: Fix truncation flickering (#45373)
It's been a little that we've noticed some flickering and other weird resizing behavior with text truncation in Zed: https://github.com/user-attachments/assets/4d5691a3-cd3d-45e0-8b96-74a4e0e273d2 https://github.com/user-attachments/assets/d1d0e587-7676-4da0-8818-f4e50f0e294e Initially, we suspected this could be due to how we calculate the length of a line to insert truncation, which is based first on the length of each individual character, and then second goes through a pass calculating the line length as a whole. This could cause mismatch and culminate in our bug. However, even though that felt like a reasonable suspicion, I realized something rather simple at some point: the `truncate` and `truncate_start` methods in the `Label` didn't use `whitespace_nowrap`. If you take Tailwind as an example, their `truncate` utility class takes `overflow: hidden; text-overflow: ellipsis; white-space: nowrap;`. This pointed out to a potential bug with `whitespace_nowrap` where that was blocking truncation entirely, even though that's technically part of what's necessary to truncate as you don't want text that will be truncated to wrap. Ultimately, what was happening was that the text element was caching its layout based on its `wrap_width` but not considering its `truncate_width`. The truncate width is essentially the new definitive width of the text based on the available space, which was never being computed. So the fix here was to add `truncate_width.is_none()` to the cache validation check, so that it only uses the cached text element size _if the truncation width is untouched_. But if that changes, we need to account for the new width. Then, in the Label component, we added `min_w_0` to allow the label div to shrink below its original size, and finally, we added `whitespace_nowrap()` as the cache check fundamentally fixed that method's problem. In a future PR, we can basically remove the `single_line()` label method because: 1) whenever you want a single label, you most likely want it to truncate, and 2) most instances of `truncate` are already followed by `single_line` in Zed today, so we can cut that part. Result is no flickering with truncated labels! https://github.com/user-attachments/assets/ae17cbde-0de7-42ca-98a4-22fcb452016b Release Notes: - Fixed a bug in GPUI where truncated text would flicker as you resized the container in which the text was in. Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
This commit is contained in:
@@ -372,11 +372,17 @@ impl TextLayout {
|
||||
(None, "".into(), TruncateFrom::End)
|
||||
};
|
||||
|
||||
// Only use cached layout if:
|
||||
// 1. We have a cached size
|
||||
// 2. wrap_width matches (or both are None)
|
||||
// 3. truncate_width is None (if truncate_width is Some, we need to re-layout
|
||||
// because the previous layout may have been computed without truncation)
|
||||
if let Some(text_layout) = element_state.0.borrow().as_ref()
|
||||
&& text_layout.size.is_some()
|
||||
&& let Some(size) = text_layout.size
|
||||
&& (wrap_width.is_none() || wrap_width == text_layout.wrap_width)
|
||||
&& truncate_width.is_none()
|
||||
{
|
||||
return text_layout.size.unwrap();
|
||||
return size;
|
||||
}
|
||||
|
||||
let mut line_wrapper = cx.text_system().line_wrapper(text_style.font(), font_size);
|
||||
|
||||
@@ -241,10 +241,16 @@ impl RenderOnce for LabelLike {
|
||||
.when(self.strikethrough, |this| this.line_through())
|
||||
.when(self.single_line, |this| this.whitespace_nowrap())
|
||||
.when(self.truncate, |this| {
|
||||
this.overflow_x_hidden().text_ellipsis()
|
||||
this.min_w_0()
|
||||
.overflow_x_hidden()
|
||||
.whitespace_nowrap()
|
||||
.text_ellipsis()
|
||||
})
|
||||
.when(self.truncate_start, |this| {
|
||||
this.overflow_x_hidden().text_ellipsis_start()
|
||||
this.min_w_0()
|
||||
.overflow_x_hidden()
|
||||
.whitespace_nowrap()
|
||||
.text_ellipsis_start()
|
||||
})
|
||||
.text_color(color)
|
||||
.font_weight(
|
||||
|
||||
Reference in New Issue
Block a user