@@ -13,8 +13,12 @@ use crossterm::event::{Event, KeyCode, KeyModifiers};
1313use itertools:: Itertools ;
1414use std:: ops:: Range ;
1515use tui:: {
16- backend:: Backend , layout:: Rect , style:: Modifier , text:: Span ,
17- widgets:: Clear , Frame ,
16+ backend:: Backend ,
17+ layout:: Rect ,
18+ style:: Modifier ,
19+ text:: { Spans , Text } ,
20+ widgets:: Clear ,
21+ Frame ,
1822} ;
1923
2024#[ derive( PartialEq ) ]
@@ -45,7 +49,7 @@ impl TextInputComponent {
4549 default_msg : & str ,
4650 ) -> Self {
4751 Self {
48- msg : String :: default ( ) ,
52+ msg : String :: new ( ) ,
4953 visible : false ,
5054 theme,
5155 key_config,
@@ -125,17 +129,24 @@ impl TextInputComponent {
125129 self . title = t;
126130 }
127131
128- fn get_draw_text ( & self ) -> Vec < Span > {
132+ fn get_draw_text ( & self ) -> Text {
129133 let style = self . theme . text ( true , false ) ;
130134
131- let mut txt = Vec :: new ( ) ;
135+ let mut txt = Text :: default ( ) ;
132136 // The portion of the text before the cursor is added
133137 // if the cursor is not at the first character.
134138 if self . cursor_position > 0 {
135- txt. push ( Span :: styled (
136- self . get_msg ( 0 ..self . cursor_position ) ,
137- style,
138- ) ) ;
139+ let text_before_cursor =
140+ self . get_msg ( 0 ..self . cursor_position ) ;
141+ let ends_in_nl = text_before_cursor. ends_with ( '\n' ) ;
142+ txt = text_append (
143+ txt,
144+ Text :: styled ( text_before_cursor, style) ,
145+ ) ;
146+ if ends_in_nl {
147+ txt. lines . push ( Spans :: default ( ) ) ;
148+ // txt = text_append(txt, Text::styled("\n\r", style));
149+ }
139150 }
140151
141152 let cursor_str = self
@@ -147,27 +158,36 @@ impl TextInputComponent {
147158 } ) ;
148159
149160 if cursor_str == "\n " {
150- txt. push ( Span :: styled (
151- "\u{21b5} " ,
152- self . theme
153- . text ( false , false )
154- . add_modifier ( Modifier :: UNDERLINED ) ,
155- ) ) ;
161+ txt = text_append (
162+ txt,
163+ Text :: styled (
164+ "\u{21b5} \n \r " ,
165+ self . theme
166+ . text ( false , false )
167+ . add_modifier ( Modifier :: UNDERLINED ) ,
168+ ) ,
169+ ) ;
170+ } else {
171+ txt = text_append (
172+ txt,
173+ Text :: styled (
174+ cursor_str,
175+ style. add_modifier ( Modifier :: UNDERLINED ) ,
176+ ) ,
177+ ) ;
156178 }
157179
158- txt. push ( Span :: styled (
159- cursor_str,
160- style. add_modifier ( Modifier :: UNDERLINED ) ,
161- ) ) ;
162-
163180 // The final portion of the text is added if there are
164181 // still remaining characters.
165182 if let Some ( pos) = self . next_char_position ( ) {
166183 if pos < self . msg . len ( ) {
167- txt. push ( Span :: styled (
168- self . get_msg ( pos..self . msg . len ( ) ) ,
169- style,
170- ) ) ;
184+ txt = text_append (
185+ txt,
186+ Text :: styled (
187+ self . get_msg ( pos..self . msg . len ( ) ) ,
188+ style,
189+ ) ,
190+ ) ;
171191 }
172192 }
173193
@@ -182,6 +202,26 @@ impl TextInputComponent {
182202 }
183203}
184204
205+ // merges last line of `txt` with first of `append` so we do not generate unneeded newlines
206+ fn text_append < ' a > ( txt : Text < ' a > , append : Text < ' a > ) -> Text < ' a > {
207+ let mut txt = txt;
208+ if let Some ( last_line) = txt. lines . last_mut ( ) {
209+ if let Some ( first_line) = append. lines . first ( ) {
210+ last_line. 0 . extend ( first_line. 0 . clone ( ) ) ;
211+ }
212+
213+ if append. lines . len ( ) > 1 {
214+ for line in 1 ..append. lines . len ( ) {
215+ let spans = append. lines [ line] . clone ( ) ;
216+ txt. lines . push ( spans) ;
217+ }
218+ }
219+ } else {
220+ txt = append
221+ }
222+ txt
223+ }
224+
185225impl DrawableComponent for TextInputComponent {
186226 fn draw < B : Backend > (
187227 & self ,
@@ -190,10 +230,10 @@ impl DrawableComponent for TextInputComponent {
190230 ) -> Result < ( ) > {
191231 if self . visible {
192232 let txt = if self . msg . is_empty ( ) {
193- vec ! [ Span :: styled(
233+ Text :: styled (
194234 self . default_msg . as_str ( ) ,
195235 self . theme . text ( false , false ) ,
196- ) ]
236+ )
197237 } else {
198238 self . get_draw_text ( )
199239 } ;
@@ -311,7 +351,7 @@ impl Component for TextInputComponent {
311351#[ cfg( test) ]
312352mod tests {
313353 use super :: * ;
314- use tui:: style:: Style ;
354+ use tui:: { style:: Style , text :: Span } ;
315355
316356 #[ test]
317357 fn test_smoke ( ) {
@@ -350,9 +390,9 @@ mod tests {
350390
351391 let txt = comp. get_draw_text ( ) ;
352392
353- assert_eq ! ( txt. len( ) , 1 ) ;
354- assert_eq ! ( get_text( & txt[ 0 ] ) , Some ( "a" ) ) ;
355- assert_eq ! ( get_style( & txt[ 0 ] ) , Some ( & underlined) ) ;
393+ assert_eq ! ( txt. lines [ 0 ] . 0 . len( ) , 1 ) ;
394+ assert_eq ! ( get_text( & txt. lines [ 0 ] . 0 [ 0 ] ) , Some ( "a" ) ) ;
395+ assert_eq ! ( get_style( & txt. lines [ 0 ] . 0 [ 0 ] ) , Some ( & underlined) ) ;
356396 }
357397
358398 #[ test]
@@ -375,11 +415,14 @@ mod tests {
375415
376416 let txt = comp. get_draw_text ( ) ;
377417
378- assert_eq ! ( txt. len( ) , 2 ) ;
379- assert_eq ! ( get_text( & txt[ 0 ] ) , Some ( "a" ) ) ;
380- assert_eq ! ( get_style( & txt[ 0 ] ) , Some ( & not_underlined) ) ;
381- assert_eq ! ( get_text( & txt[ 1 ] ) , Some ( " " ) ) ;
382- assert_eq ! ( get_style( & txt[ 1 ] ) , Some ( & underlined) ) ;
418+ assert_eq ! ( txt. lines[ 0 ] . 0 . len( ) , 2 ) ;
419+ assert_eq ! ( get_text( & txt. lines[ 0 ] . 0 [ 0 ] ) , Some ( "a" ) ) ;
420+ assert_eq ! (
421+ get_style( & txt. lines[ 0 ] . 0 [ 0 ] ) ,
422+ Some ( & not_underlined)
423+ ) ;
424+ assert_eq ! ( get_text( & txt. lines[ 0 ] . 0 [ 1 ] ) , Some ( " " ) ) ;
425+ assert_eq ! ( get_style( & txt. lines[ 0 ] . 0 [ 1 ] ) , Some ( & underlined) ) ;
383426 }
384427
385428 #[ test]
@@ -401,12 +444,14 @@ mod tests {
401444
402445 let txt = comp. get_draw_text ( ) ;
403446
404- assert_eq ! ( txt. len( ) , 4 ) ;
405- assert_eq ! ( get_text( & txt[ 0 ] ) , Some ( "a" ) ) ;
406- assert_eq ! ( get_text( & txt[ 1 ] ) , Some ( "\u{21b5} " ) ) ;
407- assert_eq ! ( get_style( & txt[ 1 ] ) , Some ( & underlined) ) ;
408- assert_eq ! ( get_text( & txt[ 2 ] ) , Some ( "\n " ) ) ;
409- assert_eq ! ( get_text( & txt[ 3 ] ) , Some ( "b" ) ) ;
447+ assert_eq ! ( txt. lines. len( ) , 2 ) ;
448+ assert_eq ! ( txt. lines[ 0 ] . 0 . len( ) , 2 ) ;
449+ assert_eq ! ( txt. lines[ 1 ] . 0 . len( ) , 2 ) ;
450+ assert_eq ! ( get_text( & txt. lines[ 0 ] . 0 [ 0 ] ) , Some ( "a" ) ) ;
451+ assert_eq ! ( get_text( & txt. lines[ 0 ] . 0 [ 1 ] ) , Some ( "\u{21b5} " ) ) ;
452+ assert_eq ! ( get_style( & txt. lines[ 0 ] . 0 [ 1 ] ) , Some ( & underlined) ) ;
453+ assert_eq ! ( get_text( & txt. lines[ 1 ] . 0 [ 0 ] ) , Some ( "" ) ) ;
454+ assert_eq ! ( get_text( & txt. lines[ 1 ] . 0 [ 1 ] ) , Some ( "b" ) ) ;
410455 }
411456
412457 #[ test]
@@ -427,10 +472,13 @@ mod tests {
427472
428473 let txt = comp. get_draw_text ( ) ;
429474
430- assert_eq ! ( txt. len( ) , 2 ) ;
431- assert_eq ! ( get_text( & txt[ 0 ] ) , Some ( "a" ) ) ;
432- assert_eq ! ( get_style( & txt[ 0 ] ) , Some ( & underlined) ) ;
433- assert_eq ! ( get_text( & txt[ 1 ] ) , Some ( "\n b" ) ) ;
475+ assert_eq ! ( txt. lines. len( ) , 2 ) ;
476+ assert_eq ! ( txt. lines[ 0 ] . 0 . len( ) , 2 ) ;
477+ assert_eq ! ( txt. lines[ 1 ] . 0 . len( ) , 1 ) ;
478+ assert_eq ! ( get_text( & txt. lines[ 0 ] . 0 [ 0 ] ) , Some ( "a" ) ) ;
479+ assert_eq ! ( get_text( & txt. lines[ 0 ] . 0 [ 1 ] ) , Some ( "" ) ) ;
480+ assert_eq ! ( get_style( & txt. lines[ 0 ] . 0 [ 0 ] ) , Some ( & underlined) ) ;
481+ assert_eq ! ( get_text( & txt. lines[ 1 ] . 0 [ 0 ] ) , Some ( "b" ) ) ;
434482 }
435483
436484 fn get_text < ' a > ( t : & ' a Span ) -> Option < & ' a str > {
0 commit comments