gpui: Add line_clamp to truncate text after a specified number of lines (#23058)
Release Notes:
- N/A
Add this feature for some case we need keep 2 or 3 lines, but truncate.
For example the blog post summary.
- Added `line_clamp` method.
Ref: https://tailwindcss.com/docs/line-clamp
## Break changes:
- Renamed `gpui::Truncate` to `gpui::TextOverflow` to match
[CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow).
- Update `truncate` style method to match [Tailwind
CSS](https://tailwindcss.com/docs/text-overflow) behavior:
```css
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
```
<img width="538" alt="image"
src="https://github.com/user-attachments/assets/c69c4213-eac9-4087-9daa-ce7afe18c758"
/>
## Show case
<img width="816" alt="image"
src="https://github.com/user-attachments/assets/e0660290-8042-4954-b93c-c729d609484a"
/>

## Describe changes
The [second
commit](https://github.com/zed-industries/zed/commit/6b41c2772f9d1c5f4efe69fe83206a14407af4d4)
for make sure text layout to match with the line clamp. Before this
change, they may wrap many lines in sometimes. And I also make
line_clamp default to 1 if we used `truncate` to ensure no wrap.
> TODO: There is still a tiny detail that is not easy to fix. This
problem only occurs in the case of certain long words. I will think
about how to improve it later. At present, this has some flaws but does
not affect the use.
This commit is contained in:
@@ -1677,6 +1677,7 @@ impl Interactivity {
|
||||
FONT_SIZE,
|
||||
&[window.text_style().to_run(str_len)],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|mut text| text.pop())
|
||||
|
||||
@@ -2,7 +2,8 @@ use crate::{
|
||||
register_tooltip_mouse_handlers, set_tooltip_on_window, ActiveTooltip, AnyView, App, Bounds,
|
||||
DispatchPhase, Element, ElementId, GlobalElementId, HighlightStyle, Hitbox, IntoElement,
|
||||
LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size,
|
||||
TextRun, TextStyle, TooltipId, Truncate, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
|
||||
TextOverflow, TextRun, TextStyle, TooltipId, WhiteSpace, Window, WrappedLine,
|
||||
WrappedLineLayout,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
@@ -255,8 +256,6 @@ struct TextLayoutInner {
|
||||
bounds: Option<Bounds<Pixels>>,
|
||||
}
|
||||
|
||||
const ELLIPSIS: &str = "…";
|
||||
|
||||
impl TextLayout {
|
||||
fn lock(&self) -> MutexGuard<Option<TextLayoutInner>> {
|
||||
self.0.lock()
|
||||
@@ -294,19 +293,22 @@ impl TextLayout {
|
||||
None
|
||||
};
|
||||
|
||||
let (truncate_width, ellipsis) = if let Some(truncate) = text_style.truncate {
|
||||
let width = known_dimensions.width.or(match available_space.width {
|
||||
crate::AvailableSpace::Definite(x) => Some(x),
|
||||
_ => None,
|
||||
});
|
||||
let (truncate_width, ellipsis) =
|
||||
if let Some(text_overflow) = text_style.text_overflow {
|
||||
let width = known_dimensions.width.or(match available_space.width {
|
||||
crate::AvailableSpace::Definite(x) => match text_style.line_clamp {
|
||||
Some(max_lines) => Some(x * max_lines),
|
||||
None => Some(x),
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
|
||||
match truncate {
|
||||
Truncate::Truncate => (width, None),
|
||||
Truncate::Ellipsis => (width, Some(ELLIPSIS)),
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
match text_overflow {
|
||||
TextOverflow::Ellipsis(s) => (width, Some(s)),
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if let Some(text_layout) = element_state.0.lock().as_ref() {
|
||||
if text_layout.size.is_some()
|
||||
@@ -326,7 +328,11 @@ impl TextLayout {
|
||||
let Some(lines) = window
|
||||
.text_system()
|
||||
.shape_text(
|
||||
text, font_size, &runs, wrap_width, // Wrap if we know the width.
|
||||
text,
|
||||
font_size,
|
||||
&runs,
|
||||
wrap_width, // Wrap if we know the width.
|
||||
text_style.line_clamp, // Limit the number of lines if line_clamp is set.
|
||||
)
|
||||
.log_err()
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user