@@ -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
5156pub 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 }
@@ -88,13 +95,15 @@ impl TuiText {
8895 /// Lays each hard line out as a single (clipped) row instead of wrapping.
8996 pub fn truncate ( mut self ) -> Self {
9097 self . wrap = false ;
98+ self . cached_measurement = None ;
9199 self
92100 }
93101 /// Truncates each hard line at grapheme boundaries and appends `...`
94102 /// inside the width supplied during layout.
95103 pub fn truncate_with_ellipsis ( mut self ) -> Self {
96104 self . wrap = false ;
97105 self . overflow = TuiTextOverflow :: Ellipsis ;
106+ self . cached_measurement = None ;
98107 self
99108 }
100109
@@ -262,18 +271,30 @@ impl TuiElement for TuiText {
262271 _ctx : & mut TuiLayoutContext ,
263272 _app : & AppContext ,
264273 ) -> TuiSize {
265- let size = if self . is_empty ( ) {
266- constraint. clamp ( TuiSize :: ZERO )
274+ let width = constraint. max . width ;
275+ let natural_size = if self . is_empty ( ) {
276+ TuiSize :: ZERO
277+ } else if let Some ( measurement) = self
278+ . cached_measurement
279+ . filter ( |measurement| measurement. available_width == width)
280+ {
281+ measurement. natural_size
267282 } else {
268283 let paragraph = self . paragraph ( constraint. max . width ) ;
269284 let height =
270285 u16:: try_from ( paragraph. line_count ( constraint. max . width ) ) . unwrap_or ( u16:: MAX ) ;
271286 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- )
287+ let size = TuiSize :: new ( content_width, height) ;
288+ self . cached_measurement = Some ( TuiTextMeasurement {
289+ available_width : width,
290+ natural_size : size,
291+ } ) ;
292+ size
276293 } ;
294+ let size = TuiSize :: new (
295+ constraint. constrain_width ( natural_size. width ) ,
296+ constraint. constrain_height ( natural_size. height ) ,
297+ ) ;
277298 self . size = Some ( size) ;
278299 size
279300 }
0 commit comments