@@ -13,8 +13,12 @@ use crossterm::event::{Event, KeyCode, KeyModifiers};
13
13
use itertools:: Itertools ;
14
14
use std:: ops:: Range ;
15
15
use 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 ,
18
22
} ;
19
23
20
24
#[ derive( PartialEq ) ]
@@ -45,7 +49,7 @@ impl TextInputComponent {
45
49
default_msg : & str ,
46
50
) -> Self {
47
51
Self {
48
- msg : String :: default ( ) ,
52
+ msg : String :: new ( ) ,
49
53
visible : false ,
50
54
theme,
51
55
key_config,
@@ -125,17 +129,24 @@ impl TextInputComponent {
125
129
self . title = t;
126
130
}
127
131
128
- fn get_draw_text ( & self ) -> Vec < Span > {
132
+ fn get_draw_text ( & self ) -> Text {
129
133
let style = self . theme . text ( true , false ) ;
130
134
131
- let mut txt = Vec :: new ( ) ;
135
+ let mut txt = Text :: default ( ) ;
132
136
// The portion of the text before the cursor is added
133
137
// if the cursor is not at the first character.
134
138
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
+ }
139
150
}
140
151
141
152
let cursor_str = self
@@ -147,27 +158,36 @@ impl TextInputComponent {
147
158
} ) ;
148
159
149
160
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
+ ) ;
156
178
}
157
179
158
- txt. push ( Span :: styled (
159
- cursor_str,
160
- style. add_modifier ( Modifier :: UNDERLINED ) ,
161
- ) ) ;
162
-
163
180
// The final portion of the text is added if there are
164
181
// still remaining characters.
165
182
if let Some ( pos) = self . next_char_position ( ) {
166
183
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
+ ) ;
171
191
}
172
192
}
173
193
@@ -182,6 +202,26 @@ impl TextInputComponent {
182
202
}
183
203
}
184
204
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
+
185
225
impl DrawableComponent for TextInputComponent {
186
226
fn draw < B : Backend > (
187
227
& self ,
@@ -190,10 +230,10 @@ impl DrawableComponent for TextInputComponent {
190
230
) -> Result < ( ) > {
191
231
if self . visible {
192
232
let txt = if self . msg . is_empty ( ) {
193
- vec ! [ Span :: styled(
233
+ Text :: styled (
194
234
self . default_msg . as_str ( ) ,
195
235
self . theme . text ( false , false ) ,
196
- ) ]
236
+ )
197
237
} else {
198
238
self . get_draw_text ( )
199
239
} ;
@@ -311,7 +351,7 @@ impl Component for TextInputComponent {
311
351
#[ cfg( test) ]
312
352
mod tests {
313
353
use super :: * ;
314
- use tui:: style:: Style ;
354
+ use tui:: { style:: Style , text :: Span } ;
315
355
316
356
#[ test]
317
357
fn test_smoke ( ) {
@@ -350,9 +390,9 @@ mod tests {
350
390
351
391
let txt = comp. get_draw_text ( ) ;
352
392
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) ) ;
356
396
}
357
397
358
398
#[ test]
@@ -375,11 +415,14 @@ mod tests {
375
415
376
416
let txt = comp. get_draw_text ( ) ;
377
417
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) ) ;
383
426
}
384
427
385
428
#[ test]
@@ -401,12 +444,14 @@ mod tests {
401
444
402
445
let txt = comp. get_draw_text ( ) ;
403
446
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" ) ) ;
410
455
}
411
456
412
457
#[ test]
@@ -427,10 +472,13 @@ mod tests {
427
472
428
473
let txt = comp. get_draw_text ( ) ;
429
474
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" ) ) ;
434
482
}
435
483
436
484
fn get_text < ' a > ( t : & ' a Span ) -> Option < & ' a str > {
0 commit comments