Skip to content

Commit

Permalink
fix(examples): changed user_input example to work with multi-byte uni…
Browse files Browse the repository at this point in the history
…code 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>
  • Loading branch information
OkieOth and joshka committed Apr 27, 2024
1 parent 20fc0dd commit 4392759
Showing 1 changed file with 25 additions and 13 deletions.
38 changes: 25 additions & 13 deletions examples/user_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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) {
Expand Down Expand Up @@ -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,
);
Expand Down

0 comments on commit 4392759

Please sign in to comment.