Skip to content

Commit ef90a56

Browse files
committed
TUI: reuse retained text measurements
1 parent a8941be commit ef90a56

2 files changed

Lines changed: 32 additions & 10 deletions

File tree

crates/warpui_core/src/elements/tui/text.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ enum TuiTextOverflow {
4747
Clip,
4848
Ellipsis,
4949
}
50+
#[derive(Clone, Copy)]
51+
struct TuiTextMeasurement {
52+
available_width: u16,
53+
natural_size: TuiSize,
54+
}
5055

5156
pub struct TuiText {
5257
/// Styled runs that concatenate into the full text. Runs may contain hard
@@ -56,6 +61,7 @@ pub struct TuiText {
5661
style: TuiStyle,
5762
wrap: bool,
5863
overflow: TuiTextOverflow,
64+
cached_measurement: Option<TuiTextMeasurement>,
5965
size: Option<TuiSize>,
6066
origin: Option<TuiScreenPoint>,
6167
}
@@ -75,6 +81,7 @@ impl TuiText {
7581
style: TuiStyle::default(),
7682
wrap: true,
7783
overflow: TuiTextOverflow::default(),
84+
cached_measurement: None,
7885
size: None,
7986
origin: None,
8087
}
@@ -262,18 +269,30 @@ impl TuiElement for TuiText {
262269
_ctx: &mut TuiLayoutContext,
263270
_app: &AppContext,
264271
) -> TuiSize {
265-
let size = if self.is_empty() {
266-
constraint.clamp(TuiSize::ZERO)
272+
let width = constraint.max.width;
273+
let natural_size = if self.is_empty() {
274+
TuiSize::ZERO
275+
} else if let Some(measurement) = self
276+
.cached_measurement
277+
.filter(|measurement| measurement.available_width == width)
278+
{
279+
measurement.natural_size
267280
} else {
268281
let paragraph = self.paragraph(constraint.max.width);
269282
let height =
270283
u16::try_from(paragraph.line_count(constraint.max.width)).unwrap_or(u16::MAX);
271284
let content_width = u16::try_from(paragraph.line_width()).unwrap_or(u16::MAX);
272-
TuiSize::new(
273-
constraint.constrain_width(content_width),
274-
constraint.constrain_height(height),
275-
)
285+
let size = TuiSize::new(content_width, height);
286+
self.cached_measurement = Some(TuiTextMeasurement {
287+
available_width: width,
288+
natural_size: size,
289+
});
290+
size
276291
};
292+
let size = TuiSize::new(
293+
constraint.constrain_width(natural_size.width),
294+
constraint.constrain_height(natural_size.height),
295+
);
277296
self.size = Some(size);
278297
size
279298
}

crates/warpui_core/src/elements/tui/viewported_list.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ where
147147
let Some(resolved) = self.state.resolved_viewport() else {
148148
return;
149149
};
150+
let selection_is_valid = selection.validate_width(size.width);
151+
let selection_range = selection_is_valid.then(|| selection.range()).flatten();
152+
if selection_range.is_none() && !self.trim_selection_line_ends {
153+
self.selection_snapshot.borrow_mut().take();
154+
return;
155+
}
150156
let visible_height = size.height.saturating_sub(resolved.screen_offset).min(
151157
resolved
152158
.content_height
@@ -171,10 +177,7 @@ where
171177
.collect::<Vec<_>>()
172178
});
173179
*self.selection_snapshot.borrow_mut() = Some((resolved, snapshot));
174-
if !selection.validate_width(size.width) {
175-
return;
176-
}
177-
let Some(range) = selection.range() else {
180+
let Some(range) = selection_range else {
178181
return;
179182
};
180183
let viewport_bottom = resolved.window.scroll_top.saturating_add(usize::from(

0 commit comments

Comments
 (0)