fix(examples): changed user_input example to work with multi-byte unicode chars (#1069)

This is the proposed solution for issue #1068. It solves the bug in the
user_input example with multi-byte UTF-8 characters as input.

Fixes: #1068

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
This commit is contained in:
Eiko Thomas 2024-04-27 07:36:36 +02:00 committed by GitHub
parent 20fc0ddfca
commit 4392759501
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -49,7 +49,7 @@ struct App {
/// Current value of the input box
input: String,
/// Position of cursor in the editor area.
cursor_position: usize,
character_index: usize,
/// Current input mode
input_mode: InputMode,
/// History of recorded messages
@ -62,34 +62,46 @@ impl App {
input: String::new(),
input_mode: InputMode::Normal,
messages: Vec::new(),
cursor_position: 0,
character_index: 0,
}
}
fn move_cursor_left(&mut self) {
let cursor_moved_left = self.cursor_position.saturating_sub(1);
self.cursor_position = self.clamp_cursor(cursor_moved_left);
let cursor_moved_left = self.character_index.saturating_sub(1);
self.character_index = self.clamp_cursor(cursor_moved_left);
}
fn move_cursor_right(&mut self) {
let cursor_moved_right = self.cursor_position.saturating_add(1);
self.cursor_position = self.clamp_cursor(cursor_moved_right);
let cursor_moved_right = self.character_index.saturating_add(1);
self.character_index = self.clamp_cursor(cursor_moved_right);
}
fn enter_char(&mut self, new_char: char) {
self.input.insert(self.cursor_position, new_char);
let index = self.byte_index();
self.input.insert(index, new_char);
self.move_cursor_right();
}
/// Returns the byte index based on the character position.
///
/// Since each character in a string can be contain multiple bytes, it's necessary to calculate
/// the byte index based on the index of the character.
fn byte_index(&mut self) -> usize {
self.input
.char_indices()
.map(|(i, _)| i)
.nth(self.character_index)
.unwrap_or(self.input.len())
}
fn delete_char(&mut self) {
let is_not_cursor_leftmost = self.cursor_position != 0;
let is_not_cursor_leftmost = self.character_index != 0;
if is_not_cursor_leftmost {
// Method "remove" is not used on the saved text for deleting the selected char.
// Reason: Using remove on String works on bytes instead of the chars.
// Using remove would require special care because of char boundaries.
let current_index = self.cursor_position;
let current_index = self.character_index;
let from_left_to_current_index = current_index - 1;
// Getting all characters before the selected character.
@ -105,11 +117,11 @@ impl App {
}
fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
new_cursor_pos.clamp(0, self.input.len())
new_cursor_pos.clamp(0, self.input.chars().count())
}
fn reset_cursor(&mut self) {
self.cursor_position = 0;
self.character_index = 0;
}
fn submit_message(&mut self) {
@ -240,7 +252,7 @@ fn ui(f: &mut Frame, app: &App) {
f.set_cursor(
// Draw the cursor at the current position in the input field.
// This position is can be controlled via the left and right arrow key
input_area.x + app.cursor_position as u16 + 1,
input_area.x + app.character_index as u16 + 1,
// Move one line down, from the border to the input line
input_area.y + 1,
);