@@ -199,6 +199,161 @@ impl TextInputComponent {
199199 Some ( 0 )
200200 }
201201
202+ fn previous_line_start ( & self ) -> Option < usize > {
203+ let mut index = self . cursor_position ;
204+ let mut newline_count = 0 ;
205+ while index > 0 {
206+ index -= 1 ;
207+ let is_newline =
208+ matches ! ( self . msg. as_bytes( ) [ index] as char , '\n' ) ;
209+
210+ if is_newline {
211+ newline_count += 1 ;
212+ }
213+ if is_newline && newline_count == 2 {
214+ return Some ( index) ;
215+ }
216+ }
217+ None
218+ }
219+
220+ fn line_start ( & self ) -> Option < usize > {
221+ let mut index = self . cursor_position ;
222+ while index > 0 {
223+ index -= 1 ;
224+ if self . msg . as_bytes ( ) [ index] as char == '\n' {
225+ return Some ( index) ;
226+ }
227+ }
228+ None
229+ }
230+
231+ fn cursor_up ( & mut self ) {
232+ if self . cursor_position == 0 {
233+ return ;
234+ }
235+
236+ let prev_line_start = self . previous_line_start ( ) . unwrap_or ( 0 ) ;
237+ let line_start = self . line_start ( ) . unwrap_or ( 0 ) ;
238+
239+ if line_start == prev_line_start {
240+ self . cursor_position = line_start;
241+ return ;
242+ }
243+
244+ let mut dist =
245+ self . get_real_distance ( line_start, self . cursor_position ) ;
246+ self . cursor_position = prev_line_start;
247+
248+ if prev_line_start == 0 && dist > 1 {
249+ dist -= 1 ;
250+ }
251+
252+ self . cursor_forward ( dist) ;
253+ }
254+
255+ fn line_end ( & self ) -> Option < usize > {
256+ let mut index = self . cursor_position ;
257+ while index < self . msg . len ( ) - 1 {
258+ if self . msg . as_bytes ( ) [ index] as char == '\n' {
259+ return Some ( index) ;
260+ }
261+ index += 1 ;
262+ }
263+ None
264+ }
265+
266+ fn next_line_end ( & self ) -> Option < usize > {
267+ let mut index = self . cursor_position ;
268+ let mut newline_count = 0 ;
269+ while index < self . msg . len ( ) - 1 {
270+ if !self . msg . is_char_boundary ( index) {
271+ index += 1 ;
272+ continue ;
273+ }
274+ let is_newline =
275+ matches ! ( self . msg. as_bytes( ) [ index] as char , '\n' ) ;
276+
277+ if is_newline {
278+ newline_count += 1 ;
279+ }
280+ if is_newline && newline_count == 2 {
281+ return Some ( index) ;
282+ }
283+ index += 1 ;
284+ }
285+ None
286+ }
287+
288+ fn cursor_down ( & mut self ) {
289+ let line_start = self . line_start ( ) . unwrap_or ( 0 ) ;
290+ let line_end = self . line_end ( ) . unwrap_or ( self . msg . len ( ) ) ;
291+ let next_end = self . next_line_end ( ) . unwrap_or ( self . msg . len ( ) ) ;
292+
293+ if line_end == next_end {
294+ self . cursor_position = next_end;
295+ return ;
296+ }
297+
298+ if self . cursor_position - line_start > next_end {
299+ self . cursor_position = next_end;
300+ return ;
301+ }
302+
303+ let mut dist =
304+ self . get_real_distance ( line_start, self . cursor_position ) ;
305+
306+ self . cursor_position = line_end;
307+
308+ if line_start != 0 {
309+ dist -= 1 ;
310+ }
311+
312+ self . cursor_forward ( dist + 1 ) ;
313+ }
314+
315+ fn get_real_distance ( & self , start : usize , end : usize ) -> usize {
316+ let mut index = start;
317+ let mut dist = 0 ;
318+ while index <= end {
319+ if self . msg . is_char_boundary ( index) {
320+ dist += 1 ;
321+ }
322+ index += 1 ;
323+ }
324+ dist
325+ }
326+
327+ /// Move forward `distance` amount of characters stopping at a newline
328+ fn cursor_forward ( & mut self , distance : usize ) {
329+ let mut travelled = 0 ;
330+ let mut index = self . cursor_position ;
331+ while index < self . msg . len ( ) + 1 {
332+ if self . msg . is_char_boundary ( index) {
333+ travelled += 1 ;
334+ }
335+
336+ if travelled == distance {
337+ self . cursor_position = index;
338+ break ;
339+ }
340+
341+ if index == self . msg . len ( ) - 1 {
342+ self . cursor_position = index + 1 ;
343+ break ;
344+ }
345+
346+ index += 1 ;
347+
348+ if index != self . msg . len ( ) - 1
349+ && self . msg . as_bytes ( ) [ index] as char == '\n'
350+ {
351+ self . cursor_position = index;
352+ break ;
353+ }
354+ }
355+ }
356+
202357 fn backspace ( & mut self ) {
203358 if self . cursor_position > 0 {
204359 self . decr_cursor ( ) ;
@@ -303,7 +458,7 @@ impl TextInputComponent {
303458 }
304459
305460 fn draw_char_count < B : Backend > ( & self , f : & mut Frame < B > , r : Rect ) {
306- let count = self . msg . len ( ) ;
461+ let count = self . msg . chars ( ) . count ( ) ;
307462 if count > 0 {
308463 let w = Paragraph :: new ( format ! ( "[{count} chars]" ) )
309464 . alignment ( Alignment :: Right ) ;
@@ -374,7 +529,7 @@ impl DrawableComponent for TextInputComponent {
374529 area,
375530 )
376531 }
377- _ => ui:: centered_rect_absolute ( 32 , 3 , f. size ( ) ) ,
532+ _ => ui:: centered_rect_absolute ( 64 , 3 , f. size ( ) ) ,
378533 }
379534 } ;
380535
@@ -430,6 +585,14 @@ impl Component for TextInputComponent {
430585 e. modifiers . contains ( KeyModifiers :: CONTROL ) ;
431586
432587 match e. code {
588+ KeyCode :: Enter
589+ if self . input_type
590+ == InputType :: Multiline && !is_ctrl =>
591+ {
592+ self . msg . insert ( self . cursor_position , '\n' ) ;
593+ self . incr_cursor ( ) ;
594+ return Ok ( EventState :: Consumed ) ;
595+ }
433596 KeyCode :: Char ( c) if !is_ctrl => {
434597 self . msg . insert ( self . cursor_position , c) ;
435598 self . incr_cursor ( ) ;
@@ -490,6 +653,14 @@ impl Component for TextInputComponent {
490653 self . incr_cursor ( ) ;
491654 return Ok ( EventState :: Consumed ) ;
492655 }
656+ KeyCode :: Up => {
657+ self . cursor_up ( ) ;
658+ return Ok ( EventState :: Consumed ) ;
659+ }
660+ KeyCode :: Down => {
661+ self . cursor_down ( ) ;
662+ return Ok ( EventState :: Consumed ) ;
663+ }
493664 KeyCode :: Home => {
494665 self . cursor_position = 0 ;
495666 return Ok ( EventState :: Consumed ) ;
@@ -717,6 +888,55 @@ mod tests {
717888 assert_eq ! ( comp. previous_word_position( ) , None ) ;
718889 }
719890
891+ #[ test]
892+ fn test_line_change ( ) {
893+ let mut comp = TextInputComponent :: new (
894+ SharedTheme :: default ( ) ,
895+ SharedKeyConfig :: default ( ) ,
896+ "" ,
897+ "" ,
898+ false ,
899+ ) ;
900+
901+ comp. set_text ( String :: from ( "aaaaa\n äaa\n aaa\n aaa" ) ) ;
902+
903+ comp. cursor_position = 0 ;
904+ comp. cursor_down ( ) ;
905+ assert_eq ! ( comp. cursor_position, 6 ) ;
906+
907+ comp. cursor_position = 2 ;
908+ comp. cursor_down ( ) ;
909+ assert_eq ! ( comp. cursor_position, 9 ) ;
910+
911+ comp. cursor_position = 10 ;
912+ comp. cursor_down ( ) ;
913+ assert_eq ! ( comp. cursor_position, 14 ) ;
914+
915+ comp. cursor_position = 8 ;
916+ comp. cursor_down ( ) ;
917+ assert_eq ! ( comp. cursor_position, 12 ) ;
918+
919+ comp. cursor_position = 13 ;
920+ comp. cursor_down ( ) ;
921+ assert_eq ! ( comp. cursor_position, 17 ) ;
922+
923+ comp. cursor_position = 6 ;
924+ comp. cursor_up ( ) ;
925+ assert_eq ! ( comp. cursor_position, 0 ) ;
926+
927+ comp. cursor_position = 9 ;
928+ comp. cursor_up ( ) ;
929+ assert_eq ! ( comp. cursor_position, 2 ) ;
930+
931+ comp. cursor_position = 0 ;
932+ comp. cursor_up ( ) ;
933+ assert_eq ! ( comp. cursor_position, 0 ) ;
934+
935+ comp. cursor_position = 4 ;
936+ comp. cursor_up ( ) ;
937+ assert_eq ! ( comp. cursor_position, 0 ) ;
938+ }
939+
720940 #[ test]
721941 fn test_next_word_multibyte ( ) {
722942 let mut comp = TextInputComponent :: new (
0 commit comments