rush_sync_server/input/
input.rs

1// =====================================================
2// FILE: src/input/input.rs - COMPLETE FIXED (Single Widget Impl)
3// =====================================================
4
5use crate::commands::handler::CommandHandler;
6use crate::commands::history::{
7    HistoryAction, HistoryConfig, HistoryEvent, HistoryEventHandler, HistoryKeyboardHandler,
8    HistoryManager,
9};
10use crate::core::prelude::*;
11use crate::input::keyboard::{KeyAction, KeyboardManager};
12use crate::ui::cursor::{CursorKind, CursorType, UiCursor};
13use crate::ui::widget::{InputWidget, Widget};
14use ratatui::prelude::*;
15use ratatui::widgets::{Block, Borders, Padding, Paragraph};
16use unicode_segmentation::UnicodeSegmentation;
17
18pub struct InputState {
19    content: String,
20    cursor: UiCursor,
21    prompt: String,
22    history_manager: HistoryManager,
23    config: Config,
24    command_handler: CommandHandler,
25    keyboard_manager: KeyboardManager,
26    waiting_for_exit_confirmation: bool,
27    waiting_for_restart_confirmation: bool,
28}
29
30#[derive(Debug, Clone, Default)]
31pub struct InputStateBackup {
32    pub content: String,
33    pub history: Vec<String>,
34    pub cursor_pos: usize,
35}
36
37impl InputState {
38    pub fn new(config: &Config) -> Self {
39        let history_config = HistoryConfig::from_main_config(config);
40
41        Self {
42            content: String::with_capacity(100),
43            cursor: UiCursor::from_config(config, CursorKind::Input),
44            prompt: config.theme.input_cursor_prefix.clone(),
45            history_manager: HistoryManager::new(history_config.max_entries),
46            config: config.clone(),
47            command_handler: CommandHandler::new(),
48            keyboard_manager: KeyboardManager::new(),
49            waiting_for_exit_confirmation: false,
50            waiting_for_restart_confirmation: false,
51        }
52    }
53
54    pub fn update_from_config(&mut self, config: &Config) {
55        self.cursor.update_from_config(config);
56        self.prompt = config.theme.input_cursor_prefix.clone();
57        self.config = config.clone();
58
59        log::debug!(
60            "āœ… InputState cursor updated via central API: {}",
61            self.cursor.debug_info()
62        );
63    }
64
65    pub fn validate_input(&self, input: &str) -> crate::core::error::Result<()> {
66        if input.trim().is_empty() {
67            return Err(AppError::Validation(get_translation(
68                "system.input.empty",
69                &[],
70            )));
71        }
72        let grapheme_count = input.graphemes(true).count();
73        let max_length = 1024;
74
75        if grapheme_count > max_length {
76            return Err(AppError::Validation(get_translation(
77                "system.input.too_long",
78                &[&max_length.to_string()],
79            )));
80        }
81        Ok(())
82    }
83
84    pub fn reset_for_language_change(&mut self) {
85        self.waiting_for_exit_confirmation = false;
86        self.waiting_for_restart_confirmation = false;
87        self.content.clear();
88        self.history_manager.reset_position();
89        self.cursor.move_to_start();
90        log::debug!("InputState reset for language change");
91    }
92
93    fn handle_exit_confirmation(&mut self, action: KeyAction) -> Option<String> {
94        match action {
95            KeyAction::Submit => {
96                self.waiting_for_exit_confirmation = false;
97                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
98                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
99                match self.content.trim().to_lowercase().as_str() {
100                    input if input == confirm_short.to_lowercase() => {
101                        self.content.clear();
102                        Some("__EXIT__".to_string())
103                    }
104                    input if input == cancel_short.to_lowercase() => {
105                        self.clear_input();
106                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
107                    }
108                    _ => {
109                        self.clear_input();
110                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
111                    }
112                }
113            }
114            KeyAction::InsertChar(c) => {
115                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
116                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
117                if c.to_lowercase().to_string() == confirm_short.to_lowercase()
118                    || c.to_lowercase().to_string() == cancel_short.to_lowercase()
119                {
120                    self.content.clear();
121                    self.content.push(c);
122                    self.cursor.update_text_length(&self.content);
123                    self.cursor.move_to_end();
124                }
125                None
126            }
127            KeyAction::Backspace | KeyAction::Delete | KeyAction::ClearLine => {
128                self.clear_input();
129                None
130            }
131            _ => None,
132        }
133    }
134
135    fn handle_restart_confirmation(&mut self, action: KeyAction) -> Option<String> {
136        match action {
137            KeyAction::Submit => {
138                self.waiting_for_restart_confirmation = false;
139                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
140                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
141                match self.content.trim().to_lowercase().as_str() {
142                    input if input == confirm_short.to_lowercase() => {
143                        self.content.clear();
144                        Some("__RESTART__".to_string())
145                    }
146                    input if input == cancel_short.to_lowercase() => {
147                        self.clear_input();
148                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
149                    }
150                    _ => {
151                        self.clear_input();
152                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
153                    }
154                }
155            }
156            KeyAction::InsertChar(c) => {
157                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
158                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
159                if c.to_lowercase().to_string() == confirm_short.to_lowercase()
160                    || c.to_lowercase().to_string() == cancel_short.to_lowercase()
161                {
162                    self.content.clear();
163                    self.content.push(c);
164                    self.cursor.update_text_length(&self.content);
165                    self.cursor.move_to_end();
166                }
167                None
168            }
169            KeyAction::Backspace | KeyAction::Delete | KeyAction::ClearLine => {
170                self.clear_input();
171                None
172            }
173            _ => None,
174        }
175    }
176
177    fn clear_input(&mut self) {
178        self.content.clear();
179        self.history_manager.reset_position();
180        self.cursor.move_to_start();
181    }
182
183    fn handle_history_action(&mut self, action: HistoryAction) -> Option<String> {
184        match action {
185            HistoryAction::NavigatePrevious => {
186                if let Some(entry) = self.history_manager.navigate_previous() {
187                    self.content = entry;
188                    self.cursor.update_text_length(&self.content);
189                    self.cursor.move_to_end();
190                }
191            }
192            HistoryAction::NavigateNext => {
193                if let Some(entry) = self.history_manager.navigate_next() {
194                    self.content = entry;
195                    self.cursor.update_text_length(&self.content);
196                    self.cursor.move_to_end();
197                }
198            }
199        }
200        None
201    }
202
203    fn handle_history_event(&mut self, event: HistoryEvent) -> String {
204        match event {
205            HistoryEvent::Clear => {
206                self.history_manager.clear();
207                HistoryEventHandler::create_clear_response()
208            }
209            HistoryEvent::Add(entry) => {
210                self.history_manager.add_entry(entry);
211                String::new()
212            }
213            _ => String::new(),
214        }
215    }
216
217    pub fn execute(&self) -> crate::core::error::Result<String> {
218        Ok(format!(
219            "__CONFIRM_EXIT__{}",
220            get_translation("system.input.confirm_exit", &[])
221        ))
222    }
223
224    pub fn handle_key_event(&mut self, key: KeyEvent) -> Option<String> {
225        if let Some(history_action) = HistoryKeyboardHandler::get_history_action(&key) {
226            return self.handle_history_action(history_action);
227        }
228
229        if key.code == KeyCode::Esc {
230            return None;
231        }
232
233        let action = self.keyboard_manager.get_action(&key);
234
235        if self.waiting_for_exit_confirmation {
236            return self.handle_exit_confirmation(action);
237        }
238        if self.waiting_for_restart_confirmation {
239            return self.handle_restart_confirmation(action);
240        }
241
242        match action {
243            KeyAction::Submit => {
244                // āœ… KORRIGIERTE DEBUG COMMANDS - alle im gleichen match Block
245                match self.content.trim() {
246                    "cursor-debug" => {
247                        let debug_info = format!(
248                            "šŸŽÆ CURSOR COLOR DEBUG:\n\
249                            šŸ“Š Theme: {}\n\
250                            šŸŽØ Expected input_cursor_color: {}\n\
251                            šŸŽØ Actual cursor color: {}\n\
252                            šŸŽØ Actual fg color: {}\n\
253                            šŸ” Cursor details:\n\
254                            {}",
255                            self.config.current_theme_name,
256                            self.config.theme.input_cursor_color.to_name(),
257                            self.cursor.color.to_name(),
258                            self.cursor.fg.to_name(),
259                            self.cursor.debug_info()
260                        );
261                        self.content.clear();
262                        self.cursor.reset_for_empty_text();
263                        Some(debug_info)
264                    }
265
266                    "theme-config-debug" => {
267                        let debug_info = format!(
268                            "šŸ” COMPLETE THEME CONFIG DEBUG:\n\
269                            šŸ“ Current Theme: {}\n\
270                            šŸŽØ input_cursor_color: {} ā¬…ļø CONFIG VALUE\n\
271                            šŸŽØ input_cursor: {}\n\
272                            šŸŽØ input_cursor_prefix: '{}'\n\
273                            šŸŽØ output_cursor_color: {}\n\
274                            šŸŽØ output_cursor: {}\n\
275                            \nšŸŽÆ ACTUAL CURSOR STATE:\n\
276                            šŸŽØ cursor.color: {} ā¬…ļø ACTUAL VALUE\n\
277                            šŸŽÆ cursor.ctype: {:?}\n\
278                            šŸ‘ļø cursor.visible: {}",
279                            self.config.current_theme_name,
280                            self.config.theme.input_cursor_color.to_name(),
281                            self.config.theme.input_cursor,
282                            self.config.theme.input_cursor_prefix,
283                            self.config.theme.output_cursor_color.to_name(),
284                            self.config.theme.output_cursor,
285                            self.cursor.color.to_name(),
286                            self.cursor.ctype,
287                            self.cursor.is_visible()
288                        );
289                        self.content.clear();
290                        self.cursor.reset_for_empty_text();
291                        Some(debug_info)
292                    }
293
294                    "color-test" => {
295                        let test_colors = vec![
296                            "Red",
297                            "Green",
298                            "Blue",
299                            "Yellow",
300                            "Magenta",
301                            "Cyan",
302                            "LightRed",
303                            "LightGreen",
304                            "LightBlue",
305                            "LightYellow",
306                            "LightMagenta",
307                            "LightCyan",
308                            "White",
309                            "Black",
310                        ];
311
312                        let mut results = String::from("šŸŽØ COLOR CONVERSION TEST:\n");
313                        for color_name in test_colors {
314                            match crate::ui::color::AppColor::from_string(color_name) {
315                                Ok(color) => {
316                                    results.push_str(&format!(
317                                        "āœ… '{}' → '{}'\n",
318                                        color_name,
319                                        color.to_name()
320                                    ));
321                                }
322                                Err(e) => {
323                                    results
324                                        .push_str(&format!("āŒ '{}' → ERROR: {}\n", color_name, e));
325                                }
326                            }
327                        }
328
329                        self.content.clear();
330                        self.cursor.reset_for_empty_text();
331                        Some(results)
332                    }
333
334                    "full-debug" => {
335                        let (_, cursor_pos) = self.render_with_cursor();
336                        let debug_info = format!(
337                            "šŸ” FULL CURSOR DEBUG:\n\
338                            šŸŽØ Config Theme: '{}'\n\
339                            šŸ“ input_cursor: '{}'\n\
340                            šŸŽÆ Parsed Type: {:?}\n\
341                            šŸ”¤ Symbol: '{}'\n\
342                            šŸ‘ļø Is Visible: {}\n\
343                            šŸ“ Position: {}\n\
344                            šŸ–„ļø Terminal Pos: {:?}\n\
345                            šŸ”§ Match Block: {}\n\
346                            ⚔ Should Use Terminal: {}",
347                            self.config.current_theme_name,
348                            self.config.theme.input_cursor,
349                            self.cursor.ctype,
350                            self.cursor.get_symbol(),
351                            self.cursor.is_visible(),
352                            self.cursor.get_position(),
353                            cursor_pos,
354                            matches!(self.cursor.ctype, CursorType::Block),
355                            !matches!(self.cursor.ctype, CursorType::Block)
356                        );
357                        self.content.clear();
358                        self.cursor.reset_for_empty_text();
359                        Some(debug_info)
360                    }
361
362                    "term-test" => {
363                        let info = format!(
364                            "šŸ–„ļø TERMINAL INFO:\n\
365                            šŸ“ŗ Terminal: {:?}\n\
366                            šŸŽÆ Cursor Support: Testing...\n\
367                            šŸ’” Try: ESC[?25h (show cursor)\n\
368                            šŸ’” Or: Different terminal app",
369                            std::env::var("TERM").unwrap_or_else(|_| "unknown".to_string())
370                        );
371                        self.content.clear();
372                        self.cursor.reset_for_empty_text();
373                        Some(info)
374                    }
375
376                    // āœ… ALLE ANDEREN COMMANDS (nicht-debug)
377                    _ => {
378                        if self.content.is_empty() {
379                            return None;
380                        }
381                        if self.validate_input(&self.content).is_ok() {
382                            let content = std::mem::take(&mut self.content);
383                            self.cursor.reset_for_empty_text();
384                            self.history_manager.add_entry(content.clone());
385                            let result = self.command_handler.handle_input(&content);
386
387                            if let Some(event) =
388                                HistoryEventHandler::handle_command_result(&result.message)
389                            {
390                                return Some(self.handle_history_event(event));
391                            }
392                            if result.message.starts_with("__CONFIRM_EXIT__") {
393                                self.waiting_for_exit_confirmation = true;
394                                return Some(result.message.replace("__CONFIRM_EXIT__", ""));
395                            }
396                            if result.message.starts_with("__CONFIRM_RESTART__") {
397                                self.waiting_for_restart_confirmation = true;
398                                return Some(result.message.replace("__CONFIRM_RESTART__", ""));
399                            }
400                            if result.message.starts_with("__RESTART_FORCE__")
401                                || result.message.starts_with("__RESTART__")
402                            {
403                                let feedback_text =
404                                    if result.message.starts_with("__RESTART_FORCE__") {
405                                        result
406                                            .message
407                                            .replace("__RESTART_FORCE__", "")
408                                            .trim()
409                                            .to_string()
410                                    } else {
411                                        result.message.replace("__RESTART__", "").trim().to_string()
412                                    };
413                                if !feedback_text.is_empty() {
414                                    return Some(format!("__RESTART_WITH_MSG__{}", feedback_text));
415                                } else {
416                                    return Some("__RESTART__".to_string());
417                                }
418                            }
419                            if result.should_exit {
420                                return Some(format!("__EXIT__{}", result.message));
421                            }
422                            return Some(result.message);
423                        }
424                        None
425                    }
426                }
427            }
428            KeyAction::InsertChar(c) => {
429                if self.content.graphemes(true).count() < self.config.input_max_length {
430                    let byte_pos = self.cursor.get_byte_position(&self.content);
431                    self.content.insert(byte_pos, c);
432                    self.cursor.update_text_length(&self.content);
433                    self.cursor.move_right();
434                }
435                None
436            }
437            KeyAction::MoveLeft => {
438                self.cursor.move_left();
439                None
440            }
441            KeyAction::MoveRight => {
442                self.cursor.move_right();
443                None
444            }
445            KeyAction::MoveToStart => {
446                self.cursor.move_to_start();
447                None
448            }
449            KeyAction::MoveToEnd => {
450                self.cursor.move_to_end();
451                None
452            }
453            KeyAction::Backspace => {
454                if self.content.is_empty() || self.cursor.get_position() == 0 {
455                    return None;
456                }
457                let current_byte_pos = self.cursor.get_byte_position(&self.content);
458                let prev_byte_pos = self.cursor.get_prev_byte_position(&self.content);
459                if prev_byte_pos >= current_byte_pos || current_byte_pos > self.content.len() {
460                    self.cursor.update_text_length(&self.content);
461                    return None;
462                }
463                self.cursor.move_left();
464                self.content
465                    .replace_range(prev_byte_pos..current_byte_pos, "");
466                self.cursor.update_text_length(&self.content);
467                if self.content.is_empty() {
468                    self.cursor.reset_for_empty_text();
469                }
470                None
471            }
472            KeyAction::Delete => {
473                let text_length = self.content.graphemes(true).count();
474                if self.cursor.get_position() >= text_length || text_length == 0 {
475                    return None;
476                }
477                let current_byte_pos = self.cursor.get_byte_position(&self.content);
478                let next_byte_pos = self.cursor.get_next_byte_position(&self.content);
479                if current_byte_pos >= next_byte_pos || next_byte_pos > self.content.len() {
480                    self.cursor.update_text_length(&self.content);
481                    return None;
482                }
483                self.content
484                    .replace_range(current_byte_pos..next_byte_pos, "");
485                self.cursor.update_text_length(&self.content);
486                if self.content.is_empty() {
487                    self.cursor.reset_for_empty_text();
488                }
489                None
490            }
491            KeyAction::ClearLine
492            | KeyAction::ScrollUp
493            | KeyAction::ScrollDown
494            | KeyAction::PageUp
495            | KeyAction::PageDown
496            | KeyAction::Cancel
497            | KeyAction::Quit
498            | KeyAction::CopySelection
499            | KeyAction::PasteBuffer
500            | KeyAction::NoAction => None,
501        }
502    }
503
504    pub fn export_state(&self) -> InputStateBackup {
505        InputStateBackup {
506            content: self.content.clone(),
507            history: self.history_manager.get_all_entries(),
508            cursor_pos: self.cursor.get_current_position(),
509        }
510    }
511
512    pub fn import_state(&mut self, backup: InputStateBackup) {
513        self.content = backup.content;
514        self.history_manager.import_entries(backup.history);
515        self.cursor.update_text_length(&self.content);
516        log::debug!(
517            "āœ… InputState imported: {} chars, {} history entries",
518            self.content.len(),
519            self.history_manager.entry_count()
520        );
521    }
522
523    pub fn get_content(&self) -> &str {
524        &self.content
525    }
526
527    pub fn get_history_count(&self) -> usize {
528        self.history_manager.entry_count()
529    }
530
531    // =====================================================
532    // āœ… RENDERING METHODS (IN InputState impl, NICHT Widget trait!)
533    // =====================================================
534
535    /// āœ… FIXED: BLOCK-CURSOR mit korrekter Farbe
536    fn render_block_cursor(
537        &self,
538        spans: &mut Vec<Span<'static>>,
539        graphemes: &[&str],
540        cursor_pos: usize,
541        viewport_start: usize,
542        available_width: usize,
543    ) {
544        // Text vor Cursor
545        if cursor_pos > viewport_start {
546            let visible_text = graphemes[viewport_start..cursor_pos].join("");
547            if !visible_text.is_empty() {
548                spans.push(Span::styled(
549                    visible_text,
550                    Style::default().fg(self.config.theme.input_text.into()),
551                ));
552            }
553        }
554
555        // āœ… ZEICHEN AM CURSOR: Invertiert wenn sichtbar
556        let char_at_cursor = graphemes.get(cursor_pos).copied().unwrap_or(" ");
557        if self.cursor.is_visible() {
558            // Invertierung: Farben tauschen
559            spans.push(Span::styled(
560                char_at_cursor.to_string(),
561                Style::default()
562                    .fg(self.config.theme.input_bg.into()) // Hintergrund wird Vordergrund
563                    .bg(self.config.theme.input_cursor_color.into()), // āœ… FIXED: Richtige Cursor-Farbe
564            ));
565        } else {
566            // Normal: Kein Cursor sichtbar
567            spans.push(Span::styled(
568                char_at_cursor.to_string(),
569                Style::default().fg(self.config.theme.input_text.into()),
570            ));
571        }
572
573        // Text nach Cursor
574        let end_pos = (viewport_start + available_width).min(graphemes.len());
575        if cursor_pos + 1 < end_pos {
576            let remaining_text = graphemes[cursor_pos + 1..end_pos].join("");
577            if !remaining_text.is_empty() {
578                spans.push(Span::styled(
579                    remaining_text,
580                    Style::default().fg(self.config.theme.input_text.into()),
581                ));
582            }
583        }
584    }
585
586    /// āœ… NEUE METHODE: Symbol-Cursor (PIPE + UNDERSCORE)
587    fn render_symbol_cursor(
588        &self,
589        spans: &mut Vec<Span<'static>>,
590        graphemes: &[&str],
591        cursor_pos: usize,
592        viewport_start: usize,
593        available_width: usize,
594    ) {
595        let end_pos = (viewport_start + available_width).min(graphemes.len());
596
597        // Text VOR Cursor
598        if cursor_pos > viewport_start {
599            let visible_text = graphemes[viewport_start..cursor_pos].join("");
600            if !visible_text.is_empty() {
601                spans.push(Span::styled(
602                    visible_text,
603                    Style::default().fg(self.config.theme.input_text.into()),
604                ));
605            }
606        }
607
608        // āœ… CURSOR-SYMBOL mit korrekter Farbe (wenn sichtbar)
609        if self.cursor.is_visible() {
610            let cursor_symbol = self.cursor.get_symbol(); // "|" oder "_"
611            spans.push(Span::styled(
612                cursor_symbol.to_string(),
613                Style::default()
614                    .fg(self.config.theme.input_cursor_color.into()) // āœ… Richtige Farbe!
615                    .bg(self.config.theme.input_bg.into()),
616            ));
617        }
618
619        // Text NACH Cursor
620        if cursor_pos < end_pos {
621            let remaining_text = graphemes[cursor_pos..end_pos].join("");
622            if !remaining_text.is_empty() {
623                spans.push(Span::styled(
624                    remaining_text,
625                    Style::default().fg(self.config.theme.input_text.into()),
626                ));
627            }
628        }
629    }
630}
631
632// āœ… SINGLE Widget Implementation - NO DUPLICATES!
633impl Widget for InputState {
634    fn render(&self) -> Paragraph {
635        self.render_with_cursor().0
636    }
637
638    /// āœ… FIXED: PIPE-Cursor auch als eigenes Symbol rendern!
639    fn render_with_cursor(&self) -> (Paragraph, Option<(u16, u16)>) {
640        let graphemes: Vec<&str> = self.content.graphemes(true).collect();
641        let cursor_pos = self.cursor.get_position();
642        let mut spans = Vec::with_capacity(8);
643
644        // Prompt-Text aus Theme
645        let prompt_display = self.config.theme.input_cursor_prefix.clone();
646        let prompt_width = prompt_display.graphemes(true).count();
647        spans.push(Span::styled(
648            prompt_display,
649            Style::default().fg(self.config.theme.input_cursor_color.into()),
650        ));
651
652        let available_width = self
653            .config
654            .input_max_length
655            .saturating_sub(prompt_width + 4);
656
657        let viewport_start = if cursor_pos > available_width {
658            cursor_pos - available_width + 10
659        } else {
660            0
661        };
662
663        match self.cursor.ctype {
664            CursorType::Block => {
665                // BLOCK: Invertiere das Zeichen unter dem Cursor
666                self.render_block_cursor(
667                    &mut spans,
668                    &graphemes,
669                    cursor_pos,
670                    viewport_start,
671                    available_width,
672                );
673                let paragraph = Paragraph::new(Line::from(spans)).block(
674                    Block::default()
675                        .padding(Padding::new(3, 1, 1, 1))
676                        .borders(Borders::NONE)
677                        .style(Style::default().bg(self.config.theme.input_bg.into())),
678                );
679                (paragraph, None)
680            }
681            CursorType::Pipe | CursorType::Underscore => {
682                // āœ… PIPE + UNDERSCORE: Beide als eigenes Symbol rendern!
683                self.render_symbol_cursor(
684                    &mut spans,
685                    &graphemes,
686                    cursor_pos,
687                    viewport_start,
688                    available_width,
689                );
690                let paragraph = Paragraph::new(Line::from(spans)).block(
691                    Block::default()
692                        .padding(Padding::new(3, 1, 1, 1))
693                        .borders(Borders::NONE)
694                        .style(Style::default().bg(self.config.theme.input_bg.into())),
695                );
696                (paragraph, None) // āœ… KEIN Terminal-Cursor mehr!
697            }
698        }
699    }
700
701    fn handle_input(&mut self, key: KeyEvent) -> Option<String> {
702        self.handle_key_event(key)
703    }
704
705    fn as_input_state(&mut self) -> Option<&mut dyn InputWidget> {
706        Some(self)
707    }
708
709    fn get_backup_data(&self) -> Option<InputStateBackup> {
710        Some(self.export_state())
711    }
712
713    fn restore_backup_data(&mut self, backup: InputStateBackup) {
714        self.import_state(backup);
715    }
716}
717
718impl InputWidget for InputState {
719    fn update_cursor_blink(&mut self) {
720        self.cursor.update_blink();
721    }
722}